はじめに
フロントエンドエンジニアとして仕事をしているもののあまり触れる機会がないものの一つがWebpackではないでしょうか。
おおよそのケース、プロジェクトが始まるタイミングで定義されてしまっているためそんなに頻繁に扱う機会はない。
でも稀にパッケージのバージョンアップや新しいディレクトリに作成したコードや画像などのアセットがビルドに含まれないなどのトラブルで見る必要が出てくることがあります。
そんな私もWebpack 4から5にバージョンアップをつい先日行いまして非常に苦労しました。
webpackの基礎的な内容を理解してきたるべき日に備えるそんな記事となっております。
webpack公式サイト
webpackは様々な環境で使うことができる便利なツールで今回の記事で全て書くのが難しいためReact、TypeScriptを使った環境については別の記事としてご紹介しています。
webpackとは
webpackは、モジュールバンドラというツールで、主にJavaScriptのコードやその他のリソース(CSS、画像、フォントなど)を1つのバンドルファイルにまとめるために使われます。
これにより、ウェブアプリケーションを効率的に配信できるようになります。
具体的には、Webpackは以下のことを行います:
-
モジュールバンドリング
複数のJavaScriptファイルを1つの大きなファイルや複数の小さなファイル(チャンク)にまとめます。これにより、複数のリソースを読み込む回数が減り、パフォーマンスが向上します。 -
トランスパイル
ES6+のコードやTypeScript、さらにはJSX(Reactで使われる)などを、ブラウザでサポートされている形式に変換します。 -
アセットの管理
画像、フォント、スタイルシートなどの非JavaScriptリソースも処理できます。これらは適切な形式に変換されたり、URLが自動で変更されたりします。 -
コード分割
アプリケーションの異なる部分(ページごとなど)を異なるファイルとして出力することができ、最初に読み込む必要のないコードは後から必要に応じて読み込まれます。 -
プラグインとローダー
Webpackではプラグインやローダーを使って、コードの最適化や、リソースの処理方法をカスタマイズすることができます。ローダーはファイルを変換するために使用され、プラグインはビルドのプロセスを拡張します。
最小構成でwebpackを始める
ということで早速webpackを使いながら少しずつwebpackの良さを理解していきましょう。
webpack-tutorial
というディレクトリを作成しNode.jsのプロジェクトとして初期化します。
npm init
途中いろいろ聞かれますがEnter
連打で問題ありません。
webpackのインストール
下記コマンドでwebpackをインストールします。
npm i -D webpack webpack-cli
CLIから使うのでwebpack-cli
も一緒にインストールしておきました。
サンプルのコードの準備
Javascriptのサンプルコードをsrc/index.js
として用意します。
function component(name) {
const element = document.createElement('div');
element.innerHTML = `Hello ${name}`
return element;
}
document.body.appendChild(component('World'));
続けてそれを読み込むHTMLファイルを作成しプロジェクトトップに保存します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>webpack tutorial</title>
</head>
<body>
<script src="./src/index.js"></script>
</body>
</html>
作成したらindex.htmlファイルをブラウザで開いて、Hello World
と表示されることを確認します。
webpackでJavaScriptファイルのバンドル
サンプルのコードが用意できましたので早速webpackでバンドルしてみます。
npx webpack
すると新しくファイルが/dist/main.js
として作成されました。
ファイルを確認してみましょう。
整形前
document.body.appendChild(function(){const e=document.createElement("div");return e.innerHTML="Hello World",e}());
整形後
document.body.appendChild(
(function () {
const e = document.createElement('div');
return (e.innerHTML = 'Hello World'), e;
})()
);
サンプルのコードと書き方は違えど同じ処理のコードが生成されています。
それではこのコードが動くのか念のため確かめるべくHTMLファイルの読み込むjsファイルを変更して確認します。
/dist/index.html
と保存先のディレクトリをプロジェクトトップから変更した上で、jsファイルの読み込み先を修正。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>webpack tutorial</title>
</head>
<body>
- <script src="./src/index.js"></script>
+ <script src="./main.js"></script>
</body>
</html>
再びブラウザで確認します。
Hello World
と表示されてますのでwebpackでバンドルしたファイルが正常に動くことを確認できました。
ここまでwebpackに関する設定なく動きました。
webpack単体で扱う分には実はそれほど難しくない仕組みではあります。
とは言えこれだけで実務で使っていくには厳しいかと思いますので少しずつwebpackを掘り下げていきます。
ファイルの変更検知
webpackにはファイルの変更を検知して自動的に再バンドルするwatch
モードがあります。
npx webpack --watch
こうしておくことでjsファイルを書き換えた際に再バンドルしブラウザでも変更内容がすぐ反映されます。
- document.body.appendChild(component('World'));
+ document.body.appendChild(component('Test'));
古いブラウザへの対応
Babelは、JavaScriptのコンパイラ(トランスパイラ)で、最新のJavaScriptコードを古いブラウザでも動作する形に変換するためのツールです。
主にモダンなJavaScript機能を使用したコードを、互換性のないブラウザ向けに変換するために使われます。
-
トランスパイル(変換)
ES6+(ECMAScript 2015以降)の新しい構文や機能を、ES5(古いブラウザがサポートするバージョン)に変換します。
例: constやletをvarに変換、arrow functions(アロー関数)を通常の関数に変換。 -
Polyfillの提供
新しいJavaScript機能が古い環境で動作しない場合、Babelはcore-jsや@babel/polyfillを通じてその機能を再現するコードを追加できます。
例: PromiseやArray.includesなど。 -
ReactやTypeScriptのサポート
JSX(Reactで使われる特殊な構文)を通常のJavaScriptに変換できます。
TypeScriptコードもBabelを使ってJavaScriptに変換可能です(ただし型チェックは行いません)。 -
プラグインとプリセット
Babelは拡張可能な仕組みを持ち、プラグインやプリセットを通じて変換内容をカスタマイズできます。
プリセット: 一連のプラグインをまとめたもの(例: @babel/preset-env)。
プラグイン: 特定の変換を担当するモジュール(例: @babel/plugin-transform-arrow-functions)。
Babel公式
先ほどのコードではconst
が使われておりこれはES6から対応している機能となります。
そのためES5までしか動かないブラウザのバージョンでは問題となります。
そこでBabelを使いES5へ変換を行います。
webpackによるバンドルももちろん行うためBabelとwebpackの連携も必要となってきます。
Babelの導入
下記のコマンドでBabel
をインストールします。
npm i -D @babel/core @babel/preset-env
@babel/preset-env
はES6 -> ES5に変換する際に必要となります。
.babelrc
ファイルをプロジェクト直下に作成し先ほどインストールしたプリセットを指定します。
{
"presets": ["@babel/preset-env"]
}
webpackとの連携
webpackではローダーと呼ばれるプログラムを利用して変換に関する設定を行なっていきます。
ES6 -> ES5に変換するローダーとしてbabel-loader
を使うためインストールします。
npm i -D babel-loader
webpackのコンフィグでローダーを設定します。
/** @type {import('webpack').Configuration} */
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
},
],
},
};
今回の設定では.js
ファイルに関してはbabel-loader
で変換を行います。
Babelの動作確認
設定完了しましたのでwebpack
でバンドルします。
npx webpack
すると今度は出力されたjsファイルがこれまでと異なっていることがわかります。
const
も使われなくなりました。
(() => {
var e;
document.body.appendChild((((e = document.createElement('div')).innerHTML = 'Hello '.concat('Test')), e));
})();
ES5の形式でjsファイルが作られることを確認でき古いブラウザでも動くようになりました。
CSSをバンドルする
jsファイルのバンドルと古いブラウザへの対応方法について見てきました。
次はCSSをwebpackで取り扱えるようにしていきます。
ローダーのインストール
JavaScriptコードの中でCSSをインポートし、それをHTMLの<style>タグとして適用する場合に2つのローダーが必要となります。
-
css-loader
CSSファイルをWebpackが認識してバンドルに含めます。 -
style-loader
CSSをHTMLの<style>タグに挿入します。
npm i -D style-loader css-loader
webpackにローダーの設定
2つのローダーを.css
ファイルの時に扱うよう設定します。
/** @type {import('webpack').Configuration} */
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
},
+ {
+ test: /\.css$/,
+ use: ["style-loader", "css-loader"],
+ },
],
},
};
配列でローダーを定義する場合、配列の最後尾から順に適用されます。
css-loader
-> style-loader
の順で動くこととなります。
サンプルコードの準備
CSSファイルを/src/styles.css
に作成します。
body {
padding: 40px;
color: blue;
}
JavaScriptファイルでCSSファイルを読み込みます。
+ import './styles.css'
動作確認
設定完了しましたのでwebpack
でバンドルします。
npx webpack
ブラウザで確認すると文字が青になっておりCSSが正しく適用されていることがわかります。
webpackのモード
これまでwebpackの実行時に警告が出ていました。
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
modeオプションが設定されていません。そのため、Webpackはこの値をproductionにフォールバックします。
developmentまたはproductionを設定することで、各環境に適したデフォルト設定を有効にできます。
これまではproduction
としてバンドルされていました。
webpack 5では3種類設定できるようです。
- development
- production
- none
試しにdevelopment
モードでwebpackを実行してみましょう。
npx webpack --mode=development
/dist/main.js
をみてみると先ほどとは違いコメントが書かれています。
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./styles.css */ \"./src/styles.css\");\n\nfunction component(name) {\n var element = document.createElement('div');\n element.innerHTML = \"Hello \".concat(name);\n return element;\n}\ndocument.body.appendChild(component('Test'));\n\n//# sourceURL=webpack://webpack-tutorial/./src/index.js?");
index.jsのバンドル結果だというのがコメントから確認できます。
production
モードにはなかったコメントなどが入るようになり開発段階では役に立ちそうです。
さいごに
ここまでwebpackの基本的な使い方や設定方法についてご紹介しました。
webpackは非常に多機能で強力なツールですが、その分設定が少し複雑に感じることもあります。
しかし、少しずつ学んでいくことで、アプリケーションのパフォーマンス向上や開発効率を大きく改善することができます。
今回紹介した内容を参考に、自社のプロジェクトに必要な設定を追加し、webpackの力を存分に活用していきましょう。
おすすめの記事
React + TypeScriptの環境をWebpackでビルドする記事をご紹介します。