
はじめに
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 アプリが作れるようになりました。
もうコードを書かない理由は無くなりましたね _(┐「ε:)_