前書き
本記事のゴール
この記事では、 Webpack を中心とする開発環境の構築方法について理解することを目指します。
この記事で解説する内容
- Webpackの概要について説明します
- Webpackの基本設定について説明します
- 簡単な設定例を示します
この記事で解説しない内容
- Webpack関連Node.jsパッケージ(ローダー/プラグイン等)については説明しません
Webpack関連プラグインのうち特に重要なものに関しては同シリーズ別記事にて解説しています。詳しくは 「ちょっとがんばるWebサイト開発 on Webpack5」 をご参照ください。
シリーズ記事
この記事はシリーズ 「ちょっとがんばるWebサイト開発 on Webpack5」 の記事です。
シリーズ全体の概要は 以下のリンク先記事 にまとめられています。
この記事では Webpack5 (version5.X.X) を対象としています。
背景知識
Webpackとは?
Webpack とは、JavaScriptアプリケーション用の モジュールバンドラー です。最も基本的には、多数のJavaScriptファイル群をまとめて少数のJavaScriptファイル群にするという処理を実行します。
Webpackには、バンドル(=ファイル等をまとめる作業)の際にモジュールの依存関係を自動で検出するという機能があります。依存関係があるファイルも含めたバンドル(=ファイル等をまとめた結果生成されたもの)を生成してくれるため、ファイル群を効率的に管理することができます。
Webpackは、主にJavaScriptアプリケーション開発(Webサイト/アプリケーション開発・デスクトップアプリケーション開発・Node.jsパッケージ開発等)において広く利用されており、特にWebサイト/アプリケーション開発環境として人気を博しています。WebpackではJavaScript以外のWebサイト/アプリケーションファイル(HTML/CSS等)を扱うための仕組みが整えられているため、現在ではWebサイト/アプリケーション開発環境の筆頭候補になっています。
Webpackを使うと何が良いのか
Webpackを使う最大のメリットは以下の2点に要約できると考えています。
-
多数のソースファイル群をバンドルして少数の配信用ファイル群にすることで、配信/利用のコストを低減することができる
-
多様なファイルを統一的に管理することで、開発/運用の効率を向上させることができる
配信/利用のコストを低減することができる
1点目は「多数のソースファイル群をバンドルして少数の配信用ファイル群にすることで、配信/利用のコストを低減することができる」という点です。ここではWebサイト開発プロジェクトの場合について考えます。
WebサイトはHTML/CSS/JavaScript等のファイル群から構成されています。Webサイト配信者はそのファイル群をWebサーバにアップロードすることでWebサイトを公開することができ、一方でWebサイト利用者はそのファイル群をダウンロードすることでWebサイトを閲覧することができます。
Webサイト配信者の視点で考えると、Webサイト構成ファイル群をアップロードするというのは非常に面倒な作業です。開発規模がある程度大きくなると、Webサイト更新の度に必要なファイルを手動で選別するというのは現実的ではありません。
Webpackのようなモジュールバンドラーを利用することで、Webサーバにアップロードするための配信用ファイル群を自動で構成する仕組みを構築することができます。これによって、(Webサイト配信者の)Webサイト運用に係るコストを低減することができると期待されます。
Webサイト利用者の視点で考えると、Webサイト構成ファイル群に含まれるファイルの数はユーザ体験(UX)に影響する重要な数値です。ファイル数の増加に比例してHTTPリクエスト回数が増加するため、細粒度にモジュール分割されたソースファイル群をそのまま配信することはあまり得策とは言えません。
Webpackのようなモジュールバンドラーを利用することで、多数のソースファイルを少数の配信用ファイルにまとめることができます。これによって、(Webサイト利用者の)ユーザ体験を向上させることができると期待されます。
開発/運用の効率を向上させることができる
2点目は「多様なファイルを統一的に管理することで、開発/運用の効率を向上させることができる」という点です。ここでもWebサイト開発プロジェクトの場合について考えます。
Webサイトは非常に多様なファイルから構成されています。最も基本的な構成要素だけを挙げてみても、Webページ構造を宣言するHTMLファイル、Webページの静的なデザインを定義するCSSファイル、Webページの動的なデザインや各種プログラムを定義するJavaScriptファイル等があります。Webサイトによっては、HTML/CSS/JavaScriptファイルに加えて、画像/動画ファイルを掲載したり、フォントファイルを配信したりすることもあるかもしれません。
プロジェクトで利用するファイルの種類が増えると、それに応じて管理する手間も増えていくことが予想されます。というのも、ファイル毎に別個の方法で管理することが多いためです。例えば、Webページ構造の宣言に関して「EJSで実装してHTMLを生成する」とすれば EJSファイルのコンパイル作業 を行う必要がありますし、Webページのデザインの定義に関して「SCSSで実装してCSSを生成する」とすれば SCSSファイルのコンパイル作業 を行う必要があります。このように、Webサイトの配信用コードを準備するために複数種類のビルド作業が必要なことも多く、それらを手動で管理するのは煩雑です。
Webpackを利用することで、こうしたビルド作業をよりシンプルに管理することができるようになります。各種ビルド作業に関する設定(この例ではEJSファイルのコンパイル作業・SCSSファイルのコンパイル作業)をWebpack設定ファイルに記述することによって、webpack コマンド1つでビルド作業をまとめて実行することができます。npmにはWebpack開発環境に便利なツールが多数公開されており、小さな労力で大きな開発環境を構築できるという点も非常に魅力的です。
方法
前提
Webpack等のnpmパッケージの利用には Node.js が必要です。
インストールが未実施の場合には、以下のリンクからインストールしておいてください。
また、動作確認を行うための Node.js プロジェクトを作成しておきます。
適当な名前のディレクトリ(ここでは my-webpack-project )を用意した上で、そのディレクトリにおいて npm init コマンドを実行することで Node.js プロジェクトを新規作成することができます。
# my-webpack-projectディレクトリの作成
mkdir /path/to/my-webpack-project
cd /path/to/my-webpack-project
# Node.jsプロジェクトの作成
npm init
npm init コマンドを実行するとプロジェクトに関する情報を入力するよう促されますが、今回は適当な設定にしておいて問題ありません。今後必要に応じて適宜変更しましょう。
コマンド実行完了後、 /package.json という設定ファイルが新規作成されていることを確認したら事前準備完了です。
手順概要
-
npmパッケージを導入する
Webpackを利用するのに必要なnpmパッケージwebpackwebpack-cliをプロジェクトに追加します。 -
Webpackの設定ファイルを記述する
ソースコードのビルド作業に関するルールを設定ファイル/webpack.config.jsに記述します。 -
動作確認用のファイルを作成する
動作確認用のHTMLファイル/src/source.html、CSSファイル/src/css/source.css、JavaScriptファイル/src/js/source.jsを作成します。 -
ビルド作業を実行する
Webpackを実行することでビルド作業を実行し、HTMLファイル/src/target.html、CSSファイル/dist/css/target.css、JavaScriptファイル/dist/js/target.jsを生成します。
手順詳細
[手順1] npmパッケージを導入する
Webpackを利用するためには、適切なnpmパッケージをインストールする必要があります。
前提 でnpmパッケージをインストールするための環境を整備したので、手順1ではWebpackを利用するのに必要な webpack webpack-cli パッケージを(プロジェクトフォルダ my-webpack-project に)インストールします。
npm install --save-dev webpack@5 webpack-cli@5
npmパッケージをインストールすると、プロジェクトフォルダ配下の /package.json ファイルの "devDependencies" 属性に webpack webpack-cli が追加されていることを確認できると思います。
この記事では、「HTML/CSS/JavaScriptファイルを含むWebサイトのプロジェクト」におけるWebpackの設定例を示しています。動作確認を行うためには、追加で以下のNode.jsパッケージが必要になるため、同様の流れでインストールしておいてください。
- HTMLファイルのビルド作業:
html-webpack-plugin - CSSファイルのビルド作業:
css-loadermini-css-extract-plugin - JavaScriptファイルのビルド作業:
babel-loader@babel/preset-env
npm install --save-dev html-webpack-plugin css-loader mini-css-extract-plugin babel-loader @babel/preset-env
[手順2-1] Webpackの設定ファイルを作成する
Webpackは、設定ファイルに記述されたルールに従ってビルド作業を実行します。
デフォルトでは、プロジェクトフォルダ配下の /webpack.config.js ファイルを読み込んでビルド作業を実行するようになっています。
ここではデフォルト挙動に従って、 /webpack.config.js にルールを記述することにします。
まず初めに、/webpack.config.js ファイルを以下の内容で作成しましょう1。
module.exports = function() {
return {
/* Webpack Configuration ... */
};
}
module.exports = function() { return { ... } } という構文で外部に公開している関数 function() { return { ... } } の返り値 { ... } がWebpackの設定オブジェクトです。
現時点では何の設定も行われていない状態ですが、以降のパートで基本設定を順次追加していきます。
[手順2-2] Webpackのエントリ設定を追加する
1つ目の基本設定は エントリ設定 です。
エントリ設定とは、Webpackのビルド対象とするファイルを指定するための設定です。
ファイルをWebpackのビルド対象に含めることで、ファイルに対して各種変換処理を行うことができるようになります。
エントリ設定の例を以下に示します。
const path = require("path");
module.exports = function() {
return {
// ========== エントリ設定 ==========
entry: {
/**
* /src/css/source.scss -> OUTPUT_DIRECTORY/css/target.css
* /src/js/source.js -> OUTPUT_DIRECTORY/js/target.js
*/
"css/target": path.resolve(__dirname, "src/css/source.css"),
"js/target": path.resolve(__dirname, "src/js/source.js"),
}
}
}
エントリ設定は entry 属性の値として記述します。上記例では、以下の2つの設定を追加しています。
-
CSSファイルのビルド設定
/src/css/source.cssファイルをcss/targetという名前のエントリでビルド対象とする -
JavaScriptファイルのビルド設定
/src/js/source.jsファイルをjs/targetという名前のエントリでビルド対象とする
エントリ名(css/target js/target 等)は、 手順2-3 の出力設定等に参照され、出力ファイル名等を決定するのに利用されます。
上記例では { entry: { target: ... } } というオブジェクト値を指定していますが、より簡潔に文字列/文字列配列等のフォーマットで指定する方法もあります。
詳細に関してはWebpack公式サイトを確認してください。
[参考] Webpackのエントリ設定
[手順2-3] Webpackの出力設定を追加する
2つ目の基本設定は 出力設定 です。
出力設定とは、Webpackのビルド作業結果の出力先を指定するための設定です。
出力設定の例を以下に示します。
出力設定はエントリ設定と密接に関係しているため、ここでは 手順2-2 のエントリ設定も併せて追加しています。
const path = require("path");
module.exports = function() {
return {
// ========== エントリ設定 ==========
entry: {
/**
* /src/css/source.scss -> OUTPUT_DIRECTORY/css/target.css
* /src/js/source.js -> OUTPUT_DIRECTORY/js/target.js
*/
"css/target": path.resolve(__dirname, "src/css/source.css"),
"js/target": path.resolve(__dirname, "src/js/source.js"),
},
// ========== 出力設定 ==========
output: {
/**
* OUTPUT_DIRECTORY = /dist
*/
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
}
}
}
出力設定は output 属性の値として記述します。
output 属性にはオブジェクト値が指定されますが、その代表的な属性には以下のようなものがあります。
-
output.path:出力ディレクトリを指定する -
output.filename:出力ファイル名を指定する
上記例では、「/src/js/source.js ファイルに対するビルド作業結果を /dist ディレクトリ配下に js/target.js ファイルという名前で出力する」という設定を追加しています。
なお、output.filename 属性値 [name].js に含まれる [name] は テンプレート文字列 と呼ばれるもので、出力ファイル名の参照時に各エントリ名に置換されます。ここでは /src/js/source.js ファイルのエントリ名を js/target としているので、出力ファイル名は [name].js -> js/target.js となります。
出力設定には output.filename output.path に加えて様々な設定を記述することができます。
詳細に関してはWebpack公式サイトを確認してください。
[参考] Webpackの出力設定
[手順2-4] Webpackのモジュール+ローダー設定を追加する
3つ目の基本設定は モジュール設定 です。
モジュール設定とは、Webpackのビルド作業において「どのようなファイルに対してどのような変換処理を実行するか」を指定するための設定です。
変換処理を実行する関数を ローダー として定義し、モジュール設定でそのローダーを適用することを宣言することで、ソースファイルに各種変換処理を適用することができます。
モジュール設定の例を以下に示します。
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = function() {
return {
// ========== モジュール設定 ==========
module: {
rules: [
{
/**
* FILE_TYPE: CSS
* LOADER: CssLoader -> MiniCssExtractPluginLoader
*/
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
],
},
{
/**
* FILE_TYPE: JavaScript
* LOADER: BabelLoader
*/
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
"presets": [ "@babel/preset-env", ],
},
}
],
}
]
}
}
}
モジュール設定は module 属性の値として記述します。
module 属性にはオブジェクト値が指定されますが、とりわけ重要なのは module.rules 属性です。
module.rules 属性には変換処理に関するルールの配列を格納します。
各ルール(オブジェクト)を構成する属性のうち、重要度が高いものは以下の通りです。
-
test:変換処理の対象とするファイルのパターンを指定する -
exclude:変換処理の対象から除外するファイルのパターンを指定する -
use:変換処理を実行するローダーを指定する
上記例では以下の2つの設定を追加しています。
-
CSSファイルのビルド設定
拡張子が.cssであるファイルを単体のファイルとして出力する -
JavaScriptファイルのビルド設定
拡張子が.jsであるファイルに対してBabel(JavaScriptコンパイラ)を適用する
なお、CSSファイルのビルド設定にはNode.jsパッケージ css-loader mini-css-extract-plugin、 JavaScriptのビルド設定にはNode.jsパッケージ babel-loader @babel/preset-env を利用しています。各種ローダーの機能に関しては別記事に譲り、ここでは参考記事のリンクのみを載せておきます。
[参考]
Webpackの基本動作は「JavaScript等の各種ファイルを JavaScriptファイルにまとめて 出力する」ことなので、CSS等JavaScript以外の種類のファイルを出力したい場合には各種ローダー/プラグインを導入して特殊な設定をする必要があります。ここでは、mini-css-extract-plugin というプラグインを利用することで、単体CSSファイルの出力を実現しています。
ローダーの実装はやや発展的なトピックであるため、この記事では解説を省略します。
モジュールおよびローダーの詳細に関してはWebpack公式サイトを確認してください。
[参考] Webpackのモジュール+ローダー設定
[手順2-5] Webpackのプラグイン設定を追加する
4つ目の基本設定は プラグイン設定 です。
プラグイン設定とは、Webpackの標準的なビルドシステムの拡張機能を指定するための設定です。
プラグイン と呼ばれるWebpackの拡張機能を利用することで、ローダー以上に柔軟な各種変換処理および最適化処理を実行することができます。
プラグイン設定の例を以下に示します。
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const WebpackRemoveEmptyScriptsPlugin = require("webpack-remove-empty-scripts");
module.exports = function() {
return {
// ========== プラグイン設定 ==========
plugins: [
/**
* FILE_TYPE: HTML
* /src/source.html -> OUTPUT_DIRECTORY/target.html
*/
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src/source.html"),
filename: "target.html",
inject: false,
minify: false,
}),
/**
* FILE_TYPE: CSS
*/
new MiniCssExtractPlugin(),
/**
* FILE_TYPE: JavaScript
*/
new WebpackRemoveEmptyScriptsPlugin(),
]
}
}
プラグイン設定は plugins 属性の値として記述します。
plugins 属性にはプラグイン(オブジェクト)の配列を格納します。
上記例では、以下の3つの設定を追加しています。
-
HTMLファイルのビルド設定
HTMLファイルを配信用ディレクトリに出力する(/src/source.html -> /dist/target.html) -
CSSファイルのビルド設定
CSSファイルを配信用ディレクトリに出力する -
不要JavaScriptファイルの削除設定
ビルド作業中に生成された不要なJavaScriptファイルを削除する
なお、HTMLファイルのビルド設定にはNode.jsパッケージ html-webpack-plugin 、CSSファイルのビルド設定にはNode.jsパッケージ mini-css-extract-plugin 、不要JavaScriptファイルの削除設定にはNode.jsパッケージ webpack-remove-empty-scripts を利用しています。
Webpackの基本動作は「JavaScript等の各種ファイルを JavaScriptファイルにまとめて 出力する」ことなので、HTML等JavaScript以外の種類のファイルを出力したい場合には各種ローダー/プラグインを導入して特殊な設定をする必要があります。ここでは、html-webpack-plugin というプラグインを利用することで、単体HTMLファイルの出力を実現しています。
プラグインの実装はやや発展的なトピックであるため、この記事では解説を省略します。
プラグイン設定の詳細に関してはWebpack公式サイトを確認してください。
[参考] Webpackのプラグイン設定
[手順2-6] Webpackのモード設定を追加する
5つ目の基本設定は モード設定 です。
モード設定とは、Webpackのビルド作業のモードを指定する設定です。
開発モード("development")または本番モード("production")を指定することで、各種最適化処理の実行可否を決めることができます2。
モード設定の例を以下に示します。
module.exports = function() {
return {
// ========== モード設定 ==========
mode: "development",
}
}
モード設定は mode 属性の値として記述します。
上記例では「開発モードを指定する(最適化処理を実行しない)」という設定を追加しています。
モード設定の詳細に関してはWebpack公式サイトを確認してください。
[参考] Webpackのモード設定
[手順2-7] Webpackの各種設定を統合する
手順2-1〜2-6ではWebpackの各種設定(エントリ/出力/モジュール/プラグイン/モード設定)について確認しました。
手順2-7では、これらの設定をまとめた完全なWebpack設定ファイルを示します。
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const WebpackRemoveEmptyScriptsPlugin = require("webpack-remove-empty-scripts");
module.exports = function() {
return {
// ========== モード設定 ==========
mode: "development",
// ========== エントリ設定 ==========
entry: {
/**
* /src/css/source.scss -> OUTPUT_DIRECTORY/css/target.css
* /src/js/source.js -> OUTPUT_DIRECTORY/js/target.js
*/
"css/target": path.resolve(__dirname, "src/css/source.css"),
"js/target": path.resolve(__dirname, "src/js/source.js"),
},
// ========== 出力設定 ==========
output: {
/**
* OUTPUT_DIRECTORY = /dist
*/
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
// ========== モジュール設定 ==========
module: {
rules: [
{
/**
* FILE_TYPE: CSS
* LOADER: CssLoader -> MiniCssExtractPluginLoader
*/
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
],
},
{
/**
* FILE_TYPE: JavaScript
* LOADER: BabelLoader
*/
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
"presets": [ "@babel/preset-env", ],
},
}
],
}
]
},
// ========== プラグイン設定 ==========
plugins: [
/**
* FILE_TYPE: HTML
* /src/source.html -> OUTPUT_DIRECTORY/target.html
*/
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src/source.html"),
filename: "target.html",
inject: false,
minify: false,
}),
/**
* FILE_TYPE: CSS
*/
new MiniCssExtractPlugin(),
/**
* FILE_TYPE: JavaScript
*/
new WebpackRemoveEmptyScriptsPlugin(),
],
}
}
今回は、3つのファイル /src/source.html /src/css/source.css /src/js/source.js をWebpackのビルド対象とするような設定を記述しています。
[手順3] 動作確認用のファイルを作成する
手順2-7の設定では、3つのファイル /src/source.html /src/css/source.css /src/js/source.js を含むプロジェクトを想定していました。
手順3では、動作確認のためにこの3つのファイルを作成していきます。
ファイル内容はどんなものでも構いませんが、ここでは例として以下のように記述することにします(折りたたみ要素の中にあります)。
HTMLファイルの実装例
<!doctype html>
<html>
<head>
<title>My HTML Project</title>
<link rel="stylesheet" href="./css/target.css">
</head>
<body>
<h1>Hello, Webpack!</h1>
<script src="./js/target.js"></script>
</body>
</html>
CSSファイルの実装例
h1 {
font-size: 100px;
}
JavaScriptファイルの実装例
const MESSAGE = "Hello, Webpack!";
console.log(MESSAGE);
[手順4] ビルド作業を実行する
手順1〜3でWebpackのビルド作業の準備が完了しました!
それではWebpackのビルド作業を実行してみましょう。
ターミナルで npx webpack コマンドを実行することでビルド作業が開始します。
# ディレクトリ移動
cd /path/to/my-webpack-project
# Webpackコマンド実行
npx webpack
ビルドが正常に完了すれば、/dist/ ディレクトリ配下に3つのファイル /dist/target.html /dist/css/target.css /dist/js/target.js が出力されていることを確認できると思います。エントリ/出力設定等を変えながらWebpackコマンドを実行してみると、さらにWebpackの設定ファイルについて理解を深められるでしょう。
後書き
サンプルコード
/website/website-dev-on-webpack5/webpack-config-on-webpack5
-
JavaScriptに詳しい読者向けに補足すると、Webpackの設定ファイル(JavaScriptファイル)はCommonJS仕様に則った記述になっています。CommonJS仕様では、モジュールのインポートは
import { ... } from ...、エクスポートはmodule.exports = { ... }のような構文で記述します。 ↩ -
本番モード(
"production")では最適化処理が実行されますが、開発モード("development")では最適化処理は実行されません。モード設定を行わなかった場合、デフォルトでは本番モード("production")となります。難読化や空白除去等の最適化処理が走るとビルドの出力結果からデバッグすることが困難になるため、開発時には開発モード("development")にしておくのが良いでしょう。 ↩