前置き
この「Webpack?Create React App?No、Vite!!」シリーズでは、5回に分けて以下の内容を扱います。
- JSにおける「コンパイル」「バンドル」「ビルド」の理解
- 表題のツールを用いたReact開発環境構築を通じて、1の内容を理解する
- viteの技術的な凄みとその「爆速」ぶりを体感
現状以下のようなタイトルを想定しています。
①コンパイル、バンドル、ビルドを理解しよう←前回
②webpackによるReact開発環境構築[バンドラ/コンパイラ編]←今回
③webpackによるReact開発環境構築[開発サーバー/HMR編]
④Create React AppによるReact開発環境構築
⑤ViteによるReact開発環境構築とその技術的な凄み
2.webpackによるReact開発環境構築[コンパイラ/バンドラ編]
2.1.webpackとは
Webpackはフロントエンド開発のためのモジュールバンドラです。ただ、モジュールのバンドル以外にもフロントエンドの開発をするにあたって必要な様々な機能を提供します。以下はその一例です。
-
コード分割 (Code Splitting):
コードを複数のバンドルに分ける機能です。これにより、全てのコードを一度に読み込むのではなく、必要になった時点で特定のコード(バンドル)を読み込むことが可能になります。例えば、特定の画面や機能でしか使用しないJavaScriptコードを分割しておけば、その画面を開いた時だけそのコードを読み込むことができます。これにより、アプリケーションの起動時間を短縮し、パフォーマンスを向上させることができます。 -
ミニファイ (Minify):
ミニファイとは、JavaScriptやCSS、HTMLなどのファイルサイズを可能な限り小さくすることです。余分な空白や改行、コメントを削除したり、変数名を短くしたりすることで、ファイルサイズを削減します。これにより、ファイルのダウンロード時間が短縮され、ウェブアプリケーションのロード時間が速くなります。 -
機能フラグ (Feature Flags):
機能フラグとは、特定の機能をオンまたはオフにするスイッチのようなものです。これを利用して、開発中の新機能を非表示にしたり、テスト中の機能を一部のユーザーだけに表示したりすることが可能になります。 -
HMR (Hot Module Replacement):
HMRは、アプリケーションをリロードせずにモジュールを更新する機能です。つまり、コードの変更があった際にページ全体をリロードするのではなく、変更があったモジュールだけを更新します。これにより、開発者はコードの変更をすぐに確認でき、開発速度が向上します。
まずwebpackは基本的に設定ファイル(webpack.config.js)に様々な設定内容を書いていく必要があります。ひとまず主要なものとして、ローダーをおさえましょう。
ローダー(Loader)
第一回の記事で、ReactやTSなどをブラウザ上で実行するには、間にコンパイル(トランスパイル)の工程が必要だということを理解しました。
ところでwebpack自体はモジュールバンドラーなので、何も設定を加えなければ既にブラウザ上で実行可能なJSファイルをバンドルする機能しか提供しません。ただ先のようにReactを使いたいといったケースや、CSSファイルもバンドルの中にまとめ上げたいといったことがあると思います。これを実現するのがローダーです。
また、webpackではローダー意外にもバンドルに関する情報(entryとoutput)や、さまざまな用途に使用するプラグイン1等他にも様々な設定項目があり、開発者はそれらを自由にカスタマイズして使用することができます。
ただ「最低限のセットアップだけ出来ればいい」という初学者にとって「カスタマイズできる」ということはあまりメリットに感じられないかもしれません。これから実際にwebpackを使って、Reactの開発環境を作っていき、本来なら最低限のセットアップにどれだけの設定が必要か体験しましょう。
今回はたくさんあるwebpackの機能のうち、特にビルド(バンドル、コンパイル)、開発サーバー、HMRに焦点を当てて、それぞれのツールがこれらをどのように実現するのかを見ていきます。
ここからの説明は、以下のリポジトリにあるReactを用いたコンポーネント志向の簡単なアプリをベースに進めます。手元に置いていただいて、確認しながら進めていただけるといいと思います。
2.2.webpackのインストール、設定
まずは、webpackとwebpack-cliのインストールです。
npm i webpack webpack-cli --save-dev
するとpackage.jsonにインストールしたパッケージが追加されます。
{...
"devDependencies": {
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
}
...
}
--save-dev(-D)しとくと開発時だけに必要なパッケージとして登録できます。
さらにプロジェクト直下にwebpack.config.jsという設定ファイルを作ります。
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist", "assets"),
filename: "bundle.js"
},
};
設定ファイル自体がCommonJS2のモジュールになっていますね。
entryにはwebpackが依存グラフの基準とするファイルを指定します。基準を決めてあげることで、漏れなく対象ファイルを解釈してやることができます。
Entry Point (index.js)
│
└───> Module A (moduleA.js)
│ │
│ └───> Module C (moduleC.js)
│
└───> Module B (moduleB.js)
│
└───> Module D (moduleD.js)
│
└───> Module E (moduleE.js)
outputはバンドルをどこにまとめるかの指定です。
2.3.コンパイルのための設定
今回対象にするリポジトリにはJSX形式のReactコンポーネントとCSSファイルが含まれています。まずは、古いブラウザでも互換性のあるJS構文にコンパイルするための設定をしていきます。
Babelというコンパイラを使います。
例によってインストールします。
npm i -D babel-loader @babel/core
これだけではBabelは使えません。webpack側にコンパイラの設定を加えてやる必要があります。webpack自体はあくまでモジュールバンドラーです。
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist", "assets"),
filename: "bundle.js"
},
+ module: {
+ rules: [
+ {
+ test: /\.(js|jsx)$/,
+ exclude: /(node_modules)/,
+ loader: "babel-loader",
+ },
+ ],
+ },
+ resolve: {
+ extensions: ['.js', '.jsx'],
+ }
};
新たにmoduleとresolveという項目を加えています。ここにはローダー単位で該当のコンパイラの設定を書いていきます。ここではjs/jsx拡張子のファイルを対象にbabel-loaderを使ってコンパイルする設定を書いています。以下簡単に各設定値の説明です。
-
module.rules: ファイルがどのように処理されるべきかを決定するルールの配列です。ここで設定されているルールは以下です
- test: ルールが適用されるべきファイルを特定するための正規表現です。この場合、拡張子が.jsまたは.jsxのファイルが該当します。
- exclude: ルールの適用から除外するべきファイルまたはディレクトリを特定します。この場合、node_modulesディレクトリ内のファイルが除外されます。
- loader: 一致したファイルの変換に使用されるローダーを指定します。この場合、"babel-loader"が使用されます。BabelはJavaScriptのトランスパイラで、ES6やそれ以降のバージョンのコードを、古いブラウザでも動作するようにES5に変換します。
-
resolve.extensions:
このプロパティは、モジュールを解決するときにwebpackが自動的に補完する拡張子の配列を指定します。この設定では、webpackはインポート文で拡張子が省略された場合、.jsファイルと.jsxファイルを探します。
たとえば、import App from './App'という記述があった場合、Webpackはまず./App.jsを探し、それが存在しなければ./App.jsxを探します。
さらにbabelは、プリセットというものを用意してあらかじめどの種類の構文を対象にしてコンパイルするかの設定を書いてやる必要があります。
webpackの設定に直接書くこともできますが、今回はプロジェクト直下にbabel.config.jsonというbabel用の設定ファイルを作ります。
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
一つ目がES.Next2、二つ目がJSXのために必要なプリセットです。おっとインストールも忘れずに。
npm i -D @babel/preset-env @babel/preset-react
これでようやくES.nextとJSXのコンパイルの設定は終わりです。
...まだCSSが残っています。
CSSにはstyle-loaderとcss-loader3を使います。インストールしてください。
npm i -D style-loader css-loader
もちろんwebpack側の設定もします。
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist", "assets"),
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
loader: "babel-loader",
},
+ {
+ test: /\.css$/i,
+ use: ["style-loader", "css-loader"],
+ }
],
},
resolve: {
extensions: ['.js', '.jsx'],
}
};
ここまで終わってようやく
$ npx webpack --mode development
> recipes-app@1.0.0 build
> npx webpack --mode development
asset bundle.js 1.15 MiB [compared for emit] (name: main)
runtime modules 1.07 KiB 6 modules
javascript modules 1.09 MiB
modules by path ./node_modules/ 1.09 MiB
modules by path ./node_modules/style-loader/dist/runtime/*.js 5.84 KiB 6 modules
modules by path ./node_modules/react/ 85.7 KiB 2 modules
modules by path ./node_modules/react-dom/ 1000 KiB 2 modules
modules by path ./node_modules/scheduler/ 17.3 KiB 2 modules
modules by path ./node_modules/css-loader/dist/runtime/*.js 2.31 KiB 2 modules
modules by path ./src/ 5.27 KiB
modules by path ./src/components/*.jsx 2.93 KiB 6 modules
modules by path ./src/components/*.css 2.08 KiB
./src/components/Menu.css 1.13 KiB [built] [code generated]
./node_modules/css-loader/dist/cjs.js!./src/components/Menu.css 967 bytes [built] [code generated]
./src/index.js 267 bytes [built] [code generated]
./data/recipes.json 1.09 KiB [built] [code generated]
webpack 5.88.1 compiled successfully in 1217 ms
ビルドできます。dist/assetsの中にbundle.jsというクソでかファイルが生成されたと思います。それがバンドルです。
ただこれだけでは開発には少々不便です。更新した内容を見るにはいちいちbuildのためのコマンドを叩かなくてはなりません。
そこでHMRと開発サーバーの出番です。
次回は、②webpackによるReact開発環境構築[開発サーバー/HMR編]ということで、webpackでこれらの設定を行なっていきます。何となくお気づきかと思いますが、ただ開発したいだけなのに結構大変ですね、、、
ただ、一度こういう経験をした上で便利ツールを使うと、あらためて後発のツールの凄みがわかったりもするものです。
次回が気になったり、ためになったと思ってくださった方は、ぜひいいねやストックしてくださると、今後の励みになります。よろしくお願いします。
参考
-
プラグインとは、バンドラーとしてのwebpackを拡張するプログラムです。ビルドの工程におけるコンパイルやバンドル以外の役割を担います。今回は割愛しますが、本番用と開発用の設定ファイルをマージするwebpack-mergeなど様々なものがあります。 ↩
-
かなり雑に言うとES.nextは比較的新しいJS構文のことを指していて、CommonJSは、Node.js上でサポートしているモジュール形式です。この辺に関しては、わかりにくいところでもあり、改めて整理するためにも記事にしようと考えています。 ↩ ↩2
-
css-loaderはcss自体のimportを可能にするもので、style-loaderはHTMLにstyleタグを埋め込むものだと思っていただけるといいかと思います。 ↩