RailsからWebpackerを外してpureなwebpack構成にしてみる その3
前回、無事に脱Webpackerして開発を進めること自体は問題なくできるようになった
しかし、ホットリロードが使えないのは開発体験としてはかなり辛いので、今回はホットリロードも使えるようにしていく
脱Webpacker後のホットリロード対応
webpack --watch
ではファイルの変更を検知して再ビルドはしてくれるが画面のリロードまではしてくれないので、やはりwebpack-dev-server
を使って実現する
実はsimpackerのリポジトリにwebpack-dev-serverを使った構成のサンプルがあるのでそちらを参考にさせていただくことにする
webpack-dev-server
がないと始まらないので yarn add --dev webpack-dev-server
で入れ、あとはwebpack.config.jsにdevServer向けの設定を書くだけでOK
webpack-dev-server
は今年8月にメジャーバージョンが4に上がり、simpackerのサンプル通りでは動かない箇所があるので、その辺をmigrationガイドを参考に書き換える
ということで書き換えたwebpack.config.jsは以下の通り(必要なところだけ記載)
const { NODE_ENV } = process.env
const isProd = NODE_ENV === "production"
module.exports = {
mode: isProd ? "production" : "development",
entry: {...},
output: {
...,
// 8080はwebpack-dev-serverがデフォルトで使用するポート
publicPath: isProd ? "/packs/" : "//localhost:8080/packs/",
},
resolve: {...},
module: {...},
plugins: [...],
devServer: {
host: "localhost",
headers: {
// CORSのアレ => https://hakozaru.com/posts/http-cors
"Access-Control-Allow-Origin": "*",
},
devMiddleware: {
publicPath: "/packs/",
},
static: {
// サーバーの起点となるディレクトリ(監視するディレクトリ)
// outputオプションのpathと同じディレクトリが指定されるはず
directory: path.resolve(__dirname, "public/packs"),
}
},
}
outputのpublicPathをwebpack-dev-serverを起動した時に使われるパスに変更し、devServerの設定を追加しただけ
devServerのpublicPathは devMiddleware
オプションの配下となり、directoryは static
オプションの配下の設定となった点が変わっている部分
(isProdの部分はsimpackerから拝借した)
あとはdev-serverの起動コマンドをpackage.jsonに書けばOK(watchは使わないので消した)
起動はwebpack経由でするのが公式で推奨されているっぽいのでそちらに従う
"scripts": {
"dev": "webpack --progress --color --mode=development",
"dev-server": "webpack serve --mode=development"
}
これで全ての準備が完了したので bin/rails s
と yarn dev-server
を実行すれば、ホットリロードな環境でWebpackerに依存せず、快適にフロントエンド開発ができるようになった
(コミット => https://github.com/hakozaru/remove_webpacker_sample/commit/df77d5dfbb7a45cd30691a2a00c4a3943d9026f5)
なお、Webpackerはプロセスを2つ立ち上げなくてもrails sだけでビルドをいい感じにやってくれていたが、どうせdocker-compose使うしそこまではやらなくても開発に支障はないと思うのでやっていない
ついでにCSSと画像、SVGもwebpack管理にする
実際の開発ではCSSとか画像も管理すると思うので一応やっておく
- 画像
- webpack4までは
file-loader
を入れてimport Img from '../images/hogehoge.png'
のようにしていたが、webpack5からはtypeオプションでasset/resource
とすることで同じ挙動にすることができるようになった(file-loader
は不要) - TSのエラーが出るので
declare module '*.png'
を定義した~~~.d.ts
を適当なフォルダに用意することをお忘れなく - https://github.com/hakozaru/remove_webpacker_sample/commit/ec6990be44fa2d5c792434983cabd31cd106dbb1 (file-loaderを使った方法。asset/resourceはCSS対応時に行う)
- webpack4までは
- SVG
- インライン(imgタグ)でもReactコンポーネントでもどちらでも使えるようにする
@svgr/webpack
とurl-loader
を入れて組み合わせればよい- 注意点はコンポーネントの方は
ReactComponent
という名前でexportされるのでasをつけて扱うことくらい - https://github.com/hakozaru/remove_webpacker_sample/commit/2a8e9d03ed3a47acb045405d19182bbf6bede8a0
- CSS(Sass)
- 流石にピュアなCSSは今時キツイので、Sassを使えるようにしておく
sass-loader style-loader css-loader sass
を入れる(sass-loaderはsassに依存しているので必要)- optionにソースマップを付与しておくと便利
- CSSのbackground-imageで指定するpngは、asset/resourceで処理するように変更しないと表示できなかった(本当はfile-loaderでもできるはずなんだけど原因がわからず)
- https://github.com/hakozaru/remove_webpacker_sample/commit/c5d7b0bfa6a8763477648c3bdf6b83cee6d7e5fc
うーんちょっとローダーの役割を完全に理解できていない感があるので、どこかでもっとちゃんと調べておこう
やっぱwebpack難しいぃ〜
2021/10/11追記
↑で原因不明だった、CSSのbackground-imageで指定した画像が表示できなかった件を調べた結果
webpack5からデフォルトで使えるようになっているAsset Modulesという機能(asset/resource
とか)が file-loader
などのアセット向けローダーと重複していたせいでうまく動作していなかった模様
公式ページの説明に従って以下のように画像向けのローダ設定を書き換えたら無事に動作した
{
test: /\.(png)$/,
use: [
{
loader: 'file-loader',
options: {
name: './images/[name].[ext]'
}
}
],
// ↓のtypeとdependencyが必要らしい
type: 'javascript/auto',
dependency: { not: ['url'] }
},
https://github.com/hakozaru/remove_webpacker_sample/commit/5e9b7335fb60ab0a15f2f0d27e4fae6fb9932199
置き換えられるならwebpack5からはローダー外してビルトインのasset/****を使うほうが良さそう