最初に
私自身Webpack使いでもなく、仕事でWebpackを使い倒すということはあまりしてこなく修正程度を行うくらいでしか触ったことがありません。 なのでもっと良い書き方があるかもしれませんが、ネットの情報を見てもDjango+Webpackの情報だとWebpackのバージョンが古かったりしたので、それらの情報を元に試行錯誤して現状最新のWebpackでフロントエンド環境の分離を実施してみました。
そのため備忘録的な感じでやり方を残しておこうと思います。
【注意】 ただしまだ開発ビルドしか作成できてません。
経緯
・SCSS使いたかった ・SPA化まではいかないけど、Djangoでのフロントエンド環境との分離をどのようにすればよいかの知見を増やしたかった
環境
必要なnpmライブラリ * webpack: 5.44.0 * webpack-bundle-tracker : 1.0.0 * webpack-cli: 4.7.2 * webpack-dev-server : 3.11.2 * webpack-merge : 5.8.0 * copy-webpack-plugin : 9.0.1 * css-loader : 5.2.6 * file-loader : 6.2.0 * mini-css-extract-plugin : 2.1.0 * rimraf : 3.0.2 * sass : 1.35.2 * sass-loader : 12.1.0 * style-loader : 3.0.0
django-webpack-loaderの導入
以下のコマンドを叩いてdjango-webpack-loaderをインストールします。
※執筆時点では1.3.0がリリースされていたのでそちらを使用しても問題ないかと思います。
$ pip install django-webpack-loader==1.1.0
Django側の設定
settings.pyに以下を追加します
WEBPACK_LOADER = { 'DEFAULT': { 'CACHE': False, 'BUNDLE_DIR_NAME': '', 'STATS_FILE': os.path.join(BASE_DIR, "frontend", "webpack-stats.json"), } }
Webpackの導入
まずはDjangoのプロジェクトルートにfrontend用のディレクトリを作成します。
私は、そのまま「frontend」と付けて作成しました。
その後frontend直下に移動して以下のコマンドを叩きpackage.jsonとnode_modulesを作成しました。
$ yarn init
その後Djangoプロジェクト直下に作成していた「templates」と「static」ディレクトリを作成したfrontendディレクトリ内に移動しました。
ここまで行うと以下のような感じになります。
frontend ├─static │ ├─fonts │ ├─images │ ├─javascripts │ │ └─app.js │ └─stylesheets │ └─app.css ├─templates │ └─app.html │ ├─node_modules ├─package.json
ここから以下のコマンドを叩いて必要なモジュールを一気にインストールしていきます。
$ yarn webpack@5.44.0 webpack-bundle-tracker@1.0.0 webpack-cli@4.7.2 webpack-dev-server@3.11.2 webpack-merge@5.8.0 copy-webpack-plugin@9.0.1 css-loader@5.2.6 file-loader@6.2.0 mini-css-extract-plugin@2.1.0 rimraf@3.0.2 sass@1.35.2 sass-loader@12.1.0 style-loader@3.0.0
Webpackの設定ファイル
frontend直下のディレクトリにwebpack.config.jsを作成して以下の内容を追加しましょう。
const path = require('path'); const { merge } = require('webpack-merge'); const BundleTracker = require('webpack-bundle-tracker'); const MiniCssExtractPlugin = require('mini-css-extract-plugin') const CopyPlugin = require("copy-webpack-plugin"); const StyleLintPlugin = require('stylelint-webpack-plugin') const baseConfig = { entry: { 'app': "./static/javascripts/app.js", // エントリポイントの分追加が必要 }, output: { filename: 'js/[name].[fullhash].bundle.js', path: path.resolve(__dirname, 'dist'), }, optimization: { splitChunks: { cacheGroups: { commons: { test: /[\\/]node_modules[\\/]/, chunks: 'initial', name: 'vendor', }, }, }, }, module: { rules: [ { test: /\.(css|scss)$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'], }, { test: /\.(eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, use: [ { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'fonts' } } ] }, { test: /\.(ico|jpg|jpeg|png|gif)(\?.*)?$/, generator: { filename: 'images/[name][ext]' }, type: 'asset/resource' } ], }, plugins: [ new BundleTracker({ path: __dirname, filename: 'webpack-stats.json', }), new MiniCssExtractPlugin({ filename: 'css/[name].[fullhash].bundle.css' }), new CopyPlugin({ patterns: [ { from: "static/images", to: "images" }, { from: "static/favicon.ico", to: "favicon.ico" }, { from: "static/sitemap.xml", to: "sitemap.xml" }, ], }), new StyleLintPlugin({ files: ['static/stylesheets/**/*.scss'], syntax: 'scss', fix: false }), ] }; const devConfig = merge(baseConfig, { mode: 'development', output: { publicPath: 'http://localhost:3000/static/', }, devServer: { port: 3000, hot: true, headers: { "Access-Control-Allow-Origin": "*" }, watchOptions: { ignored: /node_modules/ }, }, }); const productConfig = merge(baseConfig, { mode: 'production', output: { publicPath: '/static/' } }) module.exports = (env, options) => { return options.mode === 'production' ? productConfig : devConfig }
package.jsonのscriptプロパティ内を修正する
以下のように修正します。
「rm:dist」というコマンドを作成して、ビルド前に前回のビルド時に作成される/dist
フォルダを削除してからビルドを行うようにしています。
/dist
がない場合はそのままビルドが実行されます。
{ ・・・省略 "scripts": { "build": "yarn rm:dist && webpack --mode=production", "dev": "webpack serve --mode=development", "rm:dist": "rimraf dist", ・・・省略 }, ・・・省略 }
ここまで終えるとビルドできるようになるので以下のコマンドを叩いて/dist
が出力されることを確認します。
$ yarn build
Djangoテンプレートファイルwebpack用のタグを埋め込む
最後に開発サーバー起動後に画面を表示した時にビルドされる資材を読み込むようにテンプレートファイル内にWebpack用のタグを追加していきます。
これはdjango-webpack-loader
で提供されているタグで、webpack-bundle-tracker
により自動生成されるwebpack-stats.json
というファイルを読み込んで、
そのファイルに記載されている静的ファイルを読み込むようになっています。
app.html
{% load render_bundle from webpack_loader %} {% load webpack_static from webpack_loader %} <!DOCTYPE html> <html lang="ja"> <head> ・・・省略 {% render_bundle 'app' 'css' %} ・・・省略 </head> <body> <header> ・・・省略 </header> <div class="content"> ・・・省略 </div> <footer> ・・・省略 </footer> {% render_bundle 'app' 'js' %} </body> </html>
最後に
Webpack5を使ってのフロントエンド分離について、Webpack関連のプラグインやDjango側のモジュールがWebpack5対応しているか不安でしたが、テンプレートに追加した「{% load webpack_static from webpack_loader %}」を追加しても画像ファイルが読み込んでくれないという問題が発生していました。
ただこれはこちらの実装ミスだったのでパスを修正し直して解決できたので比較的大きな問題は発生せず構築できました。
これでCSSも書きやすく可読性も上がったのでフロントエンドの開発もしやすくなったかなと感じます。