【Django3.0】TypeScript と Sass でフロントエンドを書く【webpack4】
はじめに
Django で Web アプリケーションを開発する際、フロントエンドを TypeScript と Sass で書いて webpack でトランスパイル & バンドルする方法をまとめました。
ここでのフロントエンドとは、 Angular や React といったフレームワークを使わずに、 TypeScript と Sass で書いたファイルを webpack で JavaScript と CSS にトランスパイルして、静的ファイルとして Django テンプレートで読み込むことを指しています。
前提条件
- Django がインストールされており、プロジェクトとアプリケーションの作成・設定が完了している
- Node.js がインストールされており、
npm init
を行いpackage.json
が作成されている
※ Django のインストールと初期設定については、以下の記事を参考にしてください。
動作環境
OS | Version |
---|---|
Windows 10 Pro | 1909 |
Application | Version |
---|---|
PowerShell | 5.1.18362.752 |
Environment | Version |
---|---|
Node.js | 12.16.2 |
npm | 6.14.4 |
Language | Version |
---|---|
Python | 3.8.1 |
Package | Version |
---|---|
Django | 3.0.4 |
webpack | 4.43.3 |
webpack-cli | 3.3.11 |
typescript | 3.8.3 |
ts-loader | 7.0.1 |
sass | 1.26.5 |
sass-loader | 8.0.2 |
css-loader | 3.5.3 |
mini-css-extract-plugin | 0.9.0 |
postcss-loader | 3.0.0 |
autoprefixer | 9.7.6 |
最終的にやりたいこと
- Django のアプリケーション毎に static ディレクトリを作り、その中に TypeScript と Sass のファイルを置く
- webpack の entry (エントリーポイント) と output (出力先ディレクトリ) を、 Django のアプリケーション単位に設定にする
- webpack で TypeScript と Sass を、それぞれ JavaScript と CSS にトランスパイルする
- Django のテンプレートで、トランスパイルされた JavaScript と CSS を読み込む
Django プロジェクトの構成
[django_project] ├ [django_application_a] │ ├ static │ │ └ [django_application_a] │ │ ├ css │ │ │ ├ index.scss │ │ │ └ main.bundle.css ← [application_a] のバンドルファイル │ │ └ js │ │ ├ index.ts ← [application_a] のエントリーポイント │ │ └ main.bundle.js ← [application_a] のバンドルファイル │ └ templates │ └ [django_application_a] │ └ index.html ← main.bundle.js と main.bundle.css を読み込むテンプレートファイル └ [django_application_b] ├ static │ └ [django_application_b] │ ├ css │ │ ├ index.scss │ │ └ main.bundle.css ← [application_b] のバンドルファイル │ └ js │ ├ index.ts ← [application_b] のエントリーポイント │ └ main.bundle.js ← [application_b] のバンドルファイル └ templates └ [django_application_b] └ index.html ← main.bundle.js と main.bundle.css を読み込むテンプレートファイル
エントリーポイント
index.ts
import '../css/index.scss' console.log('Hello TypeScript and Sass !!');
各 Django アプリケーションのエントリーポイントの TypeScript ファイルで、 Sass ファイルをインポートします。
パッケージインストール
必要な npm パッケージをインストールします。
npm ではなく Yarn でも可です。
webpack
npm install --save-dev webpack webpack-cli
webpack
と CLI で webpack を操作するために webpack-cli
をインストールします。
TypeScript
npm install --save-dev typescript ts-loader
typescript
と webpack で TypeScript を読み込むために ts-loader
をインストールします。
Sass
npm install --save-dev sass sass-loader
sass
と webpack で Sass を読み込むために sass-loader
をインストールします。
CSS
npm install --save-dev css-loader mini-css-extract-plugin postcss-loader autoprefixer
@import
などの依存関係の解決に css-loader
、CSS ファイルを出力するために mini-css-extract-plugin
、ベンダープレフィックスを付与するために postcss-loader
と autoprefixer
をインストールします。
ちなみに、ベンダープレフィックスの付与は必須ではありません。
設定ファイル
TypeScript
tsc
コマンドで tsconfig.json
を作成し、トランスパイルの設定を記述します。
npx tsc --init
tsconfig.json
{ "compilerOptions": { "module": "es2015", // モジュール出力 "sourceMap": true, // ソースマップ出力 "target": "es5", // 変換先 ECMAScript バージョン "strict": true, // 以下のオプションを一括で有効化 // --noImplicitAny, --noImplicitThis, --alwaysStrict, --strictBindCallApply, // --strictNullChecks, --strictFunctionTypes, --strictPropertyInitialization "forceConsistentCasingInFileNames": true // ファイル名の大文字小文字を区別する } }
webpack
webpack の設定ファイルは CLI コマンドからの生成は出来ないので、ファイルを手動で作成します。
webpack.config.js
const path = require('path'); const autoprefixer = require('autoprefixer'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { // モード mode: 'development', // エントリーポイント entry: { '[application_a]/static/[application_a]/js/main': path.resolve( __dirname, '[application_a]/static/[application_a]/js/index.ts' ), '[application_b]/static/[application_b]/js/main': path.resolve( __dirname, '[application_b]/static/[application_b]/js/index.ts' ) }, // ファイル出力先 output: { // 出力先ディレクトリ path: __dirname, // 出力ファイル名 filename: '[name].bundle.js', }, // ソースマップ devtool: 'cheap-module-eval-source-map', // ローダー module: { rules: [ { test: /\.ts$/, use: 'ts-loader', }, { test: /\.scss$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { esModule: true, }, }, { loader: 'css-loader', options: { url: false, sourceMap: true, importLoaders: 2, }, }, { loader: 'postcss-loader', options: { plugins: () => [autoprefixer()], }, }, { loader: 'sass-loader', options: { implementation: require('sass'), sassOptions: { includePaths: ['./node_modules'], }, sourceMap: true, }, }, ], }, ], }, // モジュール解決 resolve: { extensions: ['.ts', '.js'], }, // プラグイン plugins: [ new MiniCssExtractPlugin({ moduleFilename: ({ name }) => `${name.replace('/js/', '/css/')}.bundle.css`, }), ], };
Django アプリケーション毎に JavaScript ファイルと CSS ファイルを出力するために、以下の点を考慮します。
entry
には Django アプリケーション単位でエントリーポイントを指定するentry
に指定するオブジェクトのキーに出力先パスとファイル名の一部を指定するoutput
で JavaScript の出力ファイル名を指定するplugins
のMiniCssExtractPlugin
で CSS の出力ファイル名を指定する
ポイントは entry
で指定したオブジェクトのキーが、 output
の [name]
と plugins
の MiniCssExtractPlugin
の name
に代入されるということです。
entry
のキーにはファイル名だけでなく、パスを含む文字列が指定できるので、 Django アプリケーション毎のエントリーポイントまでのパスをキーとすることで、結果的にバンドルファイルも Django アプリケーション毎に出力することができます。
package.json
webpack を使用する上で必ず必要なものではありませんが、 npm-script
によく使うコマンドを登録しておくと便利です。
{ "name": "django-project", "version": "0.0.1", "main": "index.js", "private": true, "devDependencies": { "autoprefixer": "^9.7.6", "css-loader": "^3.5.3", "mini-css-extract-plugin": "^0.9.0", "postcss-loader": "^3.0.0", "sass": "^1.26.5", "sass-loader": "^8.0.2", "ts-loader": "^7.0.1", "typescript": "^3.8.3", "webpack": "^4.43.0", "webpack-cli": "^3.3.11" }, "scripts": { "build": "webpack", "watch": "webpack --watch", "prod": "webpack --mode=production" } }
ここでは通常の webpack
コマンドに加え、開発時によく使う watch
オプションと、本番用の production
モードの三種類を npm-script
に追加しました。
テンプレートファイル
Django のテンプレートファイルには、 webpack でコンパイルされた JavaScript ファイルと CSS ファイルを読み込むように記述します。
{% load static %} <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="{% static "[application_name]/css/main.bundle.css" %}"> <title>Django</title> </head> <body> Hello Django !! <script src="{% static "[application_name]/js/main.bundle.js" %}"></script> </body> </html>
webpack の実行
package.json
に npm-script
として webpack のコマンドを登録している場合は、 npm run
コマンドで webpack を実行します。
npm-script
ではなく、直接 webpack
コマンドで実行しても同様なので、この辺りはお好みで。
開発用
# npm-script npm run watch # npx npx webpack --watch
watch
オプションを付けて webpack
コマンドを実行すれば、コードを変更する度に再コンパイルされるので、開発時に都度 webpack
コマンドを実行する必要が無くなります。
Web 上で、「watch
オプションを使用した場合、変更したコードの差分のみのコンパイルとなるので処理が高速化する」という記事をいくつか目にしましたが、 webpack 公式ドキュメントでは同様の記述を見付けられなかったので、真偽のほどは不明です…
本番用
# npm-script npm run prod # npx npx webpack --mode=production
mode
を production
に設定すると、 minimize
が true
になるなど、本番環境での使用を想定したコンパイルが行われます。
出力ファイル
コンパイルされたファイルは、各 Django アプリケーション毎に static ディレクトリの js / css ディレクトリ内にそれぞれ出力されます。
[django_project]/[django_application]/static/[django_application]/js/main.bundle.js [django_project]/[django_application]/static/[django_application]/css/main.bundle.css
テンプレートファイルには、出力された JavaScript ファイルと CSS ファイルを読み込むように記述します。 (テンプレートファイル を参照)
おわりに
今回紹介した内容は、単純に Django で TypeScript と Sass が使えるようになっただけではなく、 npm で配布されている様々なパッケージが使用できるようになったことを意味します。
例えば、マテリアルデザインを手軽に実装できる Material Components や 老舗の Bootstrap 、 JavaScript でグリッドを出力できる ag-Grid など、 npm でパッケージをインストールして使用すれば、 webpack がまとめてコンパイルしてくれます。
CDN から読み込んで使用する方法もありますが、ローカルにソースがあれば IDE の自動補完や定義への移動が使えるので、個人的には npm でインストールしてコンパイルする方をお勧めします。
条件を満たしていれば、 Tree Shaking によるデッドコード除去や、 Code Splitting での遅延ロードの実現など、一概に webpack より CDN の方が良いとは言い切れないと思いますので、まずはローカルインストールでもいいのではないでしょうか。
これで TypeScript + Sass + Django の組み合わせで Web アプリが作れるようになりました。
もうコードを書かない理由は無くなりましたね _(┐「ε:)_