webpackの公式ホームページにアクセスすると最初に目に留まる単語にbundle(バンドル)というものがあります。bundle your assets, bundle your scripts, bundle images….。もしwebpackのbundle(バンドル)とは一体どういう意味なのか疑問を持っている人であれば本文書が最適な読者の方です。

bundleという単語は英語では”束ねる”という意味があります。webpackは複数のファイルを束ねる処理(bundle your asset/image/scripts/styles)を行う機能を持っているツールをバンドラーと呼びます。webpackの基本機能に複数のファイルを1つに束ねるということが理解できればwebpackを理解するスピードも格段に上がります。本文書では”ファイルを束ねる”を念頭において読み進めてください。

ファイルをまとめる処理はwebpackの1つの機能にすぎませんがまずは基本となるまとめる機能を理解しその後その他の機能について理解を深めていきます
fukidashi

webpackはnpmを使ってインストールを行うのでnpmがわからない人は以下の記事を参考にしてください。

webpackはbundleする機能を持っているためモジュールバンドラーと呼ばれます。モジュールという言葉もわかりにくいですがモジュールというのはファイルです。モジュールバンドラーはつまりファイルを束ねるツールと考えることができます。ファイルにはHTMLファイル、JavaScriptファイル、CSSファイルや画像ファイルなどが含まれます。

なぜwebpackなどのツールが必要なのか?

webpackの重要な機能の一つであるファイルをまとめることがなぜ必要なのでしょうか?

必要な理由は、複数のファイルを1つのファイルに束ねることでサーバからブラウザに送信するファイル数を減らすことができ処理速度を上げることができることです。同じサイズのファイルであればファイル毎に複数送信するよりも一つのファイルで送信したほうが送信に関わる処理を減らすことができるため早く処理を終えることができるためです。最初から1つのファイルにすべてのコードを書き込めばいいのではという疑問も出るかもしれませんが、1つのファイルに書き込むよりも機能別や役割によって個別のファイルに書き込みことのほうが効率的に開発を進めることができます。機能別や役割によってファイルを別にしておくことで複数のプロジェクトで再利用することも可能です。機能毎の単体テストの実施も容易になります。

ファイルを束ねるということを中心にwebpackの説明を行ってきましたが、webpackなどのバンドラーツールはファイルを束ねる以外の機能を持っています。ファイルの内容を解析することで不必要な情報を削除したりブラウザが理解することができない言語やサポートしていない処理をブラウザが理解できる形式に変換してくれるということを行ってくれます。そのためwebpackを本当に理解するためにBundle(束ねる)以外の処理を理解する必要があります。

webpackのインストール

npmを使用してインストールを行うためにディレクトリを作成します。ここでは動作確認用なのでtestというディレクトリを作成しています。


mac $ mkdir test
mac $ cd test

npm initコマンドでpackage.jsonファイルを作成します。


mac $ npm init -yes

パッケージを開発用としてインストールする場合は、–save-devをつけてnpm installコマンドを実行します。webpackとwebpackのコマンドラインインターフェイスであるwebpack-cliの2つパッケージをインストールします。webpack-cliをインストールしなければ、webpackを実行することはできません。


mac $ npm install --save-dev webpack webpack-cli

package.jsonが未作成の状態でinstallを行うとインストール済みパッケージを確認するnpm lsやnpm listを実行するとnpm ERR! extraneous:が多数表示されます。package.jsonの作成を忘れないように進めてください。
fukidashi

インストール後、package.jsonを確認するとdeevDependenciesには、webpackとwebpack-cliの情報が追加されます。


  "devDependencies": {
    "webpack": "^5.52.0",
    "webpack-cli": "^4.8.0"
  }

以上でwebpackのインストールは完了です。

webpackコマンドの実行方法の確認

webpackがどういった処理を行うか説明する前にwebpackコマンドの実行方法について説明を行います。

webpackコマンドをインストールディレクトリで実行した場合、PATH(パス)の設定が行われていなければcommand not foundエラーが表示されます。


mac $ webpack -v
zsh: command not found: webpack

※-vはwebpackのバージョンを表示させるオプションです。動作確認を行うために-vオプションをつけて実行しています。

PATH(パス)がわからない人はこちらの文書を参考にしてください。

エラーが表示される理由は、インストールしたパッケージの実行ファイルはnode_modules/.bin/の下に保存されるためです。webpackコマンドも.binの下に保存されます。パスが設定されていない場合はwebpackコマンドを実行するためには下記のように実行ファイルの場所を指定する必要があります。


mac $./node_modules/.bin/webpack -v
webpack 5.52.0
webpack-cli 4.8.0

その他にもwebpackを実行する方法には下記の方法があります。

  • npxコマンドを利用する方法
  • package.jsonのscriptsにコマンドを記述する方法

npxコマンドを利用した実行

npxコマンドを利用する場合は、npxの後にwebpackを指定すれば実行することができます。


mac % npx webpack -v
webpack 5.39.1
webpack-cli 4.7.2

npxを使うとPATHの設定が行われていなくてもnode_modules/.bin/ディレクトリの中から指定したコマンドを自動的に探し出して実行します。そのためPATHの設定を行う必要がありません。
fukidashi

package.jsonのscriptsに記述する方法

一般的には下記のようにpackage.jsファイルのscriptsにコマンドを追加することでPATHを意識することなく実行することができます。


 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack -v"
  },

package.jsonに追加後、rpm run bulidを実行するとwebpack -vを実行することができます。webpackというコマンドを直接叩くことはなくなります。


test $ npm run build

> test@1.0.0 build /Users/mac/Document/test
> webpack -v

webpack 5.52.0
webpack-cli 4.8.0

webpackを実行する際に-vオプションは必要ないので、webpackコマンドの実行方法が確認できた後は、package.jsonは下記のように書き換えておきます。


 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },

webpackの実行

webpackコマンドの実行方法が確認できたので、実際にwebpackコマンドを実行して理解を深めていきます。


mac $ npm run build

実行するとWARNINGとERRORが下記のように出力されます。

※他にも実行ログが出力されますが、WARNINGとERRORのみ抜粋しています。


 % npm run build

> test@1.0.0 build
> webpack

assets by status 0 bytes [cached] 1 asset
WARNING in configuration
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.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

ERROR in main
Module not found: Error: Can't resolve './src' in '/Users/mac/Desktop/test'
//略

エントリーモジュールのERROR対応

ERRORについては./srcの中にエントリーモジュールを見つけることができないというエラーなので、srcディレクトリを作成し、その中にindex.jsファイルを作成します。index.jsの中身は空でかまいません。


mac $ mkdir src
mac $ cd src
mac $ touch index.js

再度npm run buildを実行します。Errorの表示が消え、WARNINGだけが残った状態になります。WARNINGについてはのちほど対応します。


test % npm run build

> test@1.0.0 build
> webpack

asset main.js 0 bytes [emitted] [minimized] (name: main)
./src/index.js 1 bytes [built] [code generated]

WARNING in configuration
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.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

実行後、新たにdistディレクトリ(distribution(配布)の略)が作成され、その中にmain.jsが生成されることが確認できます。


mac $ ls
dist			package-lock.json	src
node_modules		package.json
mac $ ls dist
main.js

webpack初期のディレクトリ構成
webpack初期のディレクトリ構成

webpackコマンドを実行するとindex.jsファイルの中身が解析、処理された結果main.jsファイルがdistディレクトリの下に自動生成されます。このmain.jsの元になるindex.jsファイルはエンドポイントと呼ばれwebpackの一連の処理の中でメインとなるJavaScriptファイルです。

初期設定ではwebpackはsrcディレクトリにindex.jsがないかチェックを行い、あれば処理を継続しdistディレクトリを作成しmain.jsファイルを生成します。

ここまでの処理では1つのファイル(index.js)から1つのファイル(main.js)が出来ただけです。後ほど複数のファイルから1つのファイルへとまとめる処理を行います。

生成されたファイルmain.jsを下記のようにHTMLで指定して使います。main.jsを生成するための元のコードが記述されているindex.jsファイルはHTMLでは利用しません。


<script src="./dist/main.js"></script>

webpackではsrcディレクトリにindex.jsファイルさえあればwebpackの設定ファイルであるwebpack.config.jsファイルがなくても処理を行うことができます
fukidashi

modeオプションのWARNINGへの対応

次にwebpack実行時に出力されていたWARNINGに対応するためにmode(モード)の設定を行います。modeにはproduction, development, noneを設定することが可能です。productionには本番、developmentは開発という意味があります。productionモードでは本番用なのでファイルを圧縮するなど開発用のdevelopmentと大きな違いがあります。以下のようにpackage.jsonのscriptsで分けることで開発と本番用を分けることも可能です。


  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack --mode development",
    "build": "webpack --mode production"
  },

この設定により、npm runでdevを指定した場合はmodeはdevelopment, buildを指定した場合のmodeはproductionで実行されます。

ここまでの設定を行うことでwebpack実行時のエラーと警告はなくなります。


mac % npm run build

> test@1.0.0 build
> webpack --mode production

asset main.js 0 bytes [compared for emit] [minimized] (name: main)
./src/index.js 1 bytes [built] [code generated]
webpack 5.39.1 compiled successfully in 169 ms

watchオプションで変更を監視

JavaScriptファイルを変更する度にnpm runコマンドを実行するは手間がかかり効率的ではありません。効率よく開発を行うためにwatchオプションが準備されています。watchオプションは、jsファイルの変更を監視することができるのでjsファイルに変更があると自動でnpm runコマンドを実行してくれます。


  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack --mode development",
    "build": "webpack --mode production",
    "watch": "webpack --mode development --watch"
  },

scriptsにwatchを追加したら、npm run watchコマンドを実行し、別のコンソールを起動もしくはエディターでindex.jsファイルを変更を行ってください。自動でnpm run watchコマンドが実行されることを確認することができます。

webpackで複数のファイルをまとめる

ERROR, WARNINGも出力されなくなり、最もシンプルな方法でのwebpackコマンドの使用方法がわかりました。ここから実際に複数のファイルをまとめる処理を行います。

ファイルをまとめる準備

testディレクトリの中にindex.htmlファイルを作成します。作成したindex.htmlファイルのscriptタグには、srcのindex.jsを指定します。


<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>webpack</title>
	<script src="src/index.js"></script>
</head>
<body>
	
</body>
</html>

index.jsにもconsole.logを記述します。


console.log('Hello World')

index.htmlをブラウザで開き、デベロッパーツールのコンソールにHello Worldが表示されることを確認します。

コンソールにHello World
コンソールにHello World

webpackの処理を行うため、npm run devを実行します。正常に終了できたら、先ほどのindex.htmlのscriptタグの指定をindex.jsからdst/main.jsに変更し、コンソールにHello Worldが表示されることを確認します。


mac  % npm run build

> test@1.0.0 build
> webpack --mode production

asset main.js 27 bytes [emitted] [minimized] (name: main)
./src/index.js 28 bytes [built] [code generated]
webpack 5.39.1 compiled successfully in 179 ms


<script src="dist/main.js"></script>

複数のファイルをまとめる処理

2つの数字を合計する関数を含むsum.jsと2つの数字を掛け合わせるmultiply.jsの2つのファイルを用意して、index.jsファイルから読み込めるようにします。

JavaScriptではsum.js, multiply.jsこの1つ1つのファイルのことをモジュールと呼びます。
fukidashi

export default function sum(a, b) {
  return a + b;
}

export default function multiply(a, b) {
  return a * b;
}

index.js内で2つのファイルを読み込み、その結果をconsoleに出力させます。


import sum from './sum.js';
import multiply from './multiply.js';

const num1 = 10;
const num2 = 5;

const result = '合計は' + sum(num1,num2) + ',掛け算は' + multiply(num1,num2);

console.log(result);
importするファイルの拡張子を省略することも可能です。import sum from ‘./sum’
fukidashi

これまではindex.jsのファイルからwebpackを通してmain.jsという名前の1つのファイルを作成してきました。しかし今回は、index.js, sum.js, multply.jsの3つのファイルが関連しているためwebpackの処理もそれら3つのファイルが関連する処理になります。

npm run watchを実行しているので変更を検知して実行のログにも3つのファイルの情報が出力されていることがわかります。


mac  % npm run dev  

> test@1.0.0 dev
> webpack --mode development

asset main.js 4.91 KiB [emitted] (name: main)
runtime modules 670 bytes 3 modules
cacheable modules 324 bytes
  ./src/index.js 211 bytes [built] [code generated]
  ./src/sum.js 54 bytes [built] [code generated]
  ./src/multiply.js 59 bytes [built] [code generated]
webpack 5.52.0 compiled successfully in 100 ms

ブラウザでindex.htmlファイルを確認します。scriptタグで指定するはindex.jsファイルではなくmain.jsだというのを忘れないようにしてください。


<script src="dist/main.js"></script>

ブラウザのconsoleには、合計は15, 掛け算は50が表示されます。

sumとmultiplyの結果表示
sumとmultiplyの結果表示

webpackを利用すれば別々だったJavaScriptファイルがmain.jsという1つファイルに束ねられて作成することがわかります。

webpackでビルドする前のsrc/index.jsファイルをscriptで指定した場合にはどのような動作になるのかも確認しておきましょう。


<script src="./src/index.js"></script>

ブラウザのコンソールを見ると”Uncaught SyntaxError: Cannot use import statement outside a module”のメッセージが表示され先程のように処理は行われません。

module.exportとexport, requireとimportの違い

JavaSCriptの勉強を始めた入門者にとって混乱する箇所の一つがexport、imortやrequireの使用方法です。あるところではexportと記述されており、あるところではmodule.exportと記述されどれが正しいのかという疑問です。webpackではどちらの構文を利用しても問題なく動作します。

multiply.jsのみ記述方法をmodule.exportsに変更してみましょう。


module.exports = function (a, b) {
  return a * b;
};

index.jsはimportからrequireに変更を行います。


import sum from './sum.js';
const multiply = require('./multiply');

const num1 = 10;
const num2 = 5;

const result = '合計は' + sum(num1, num2) + ',掛け算は' + multiply(num1, num2);

console.log(result);

2つの構文を混ぜても正常に動作することが確認できます。

modeの設定によるmain.jsファイルの違い

webpackを実行する際にmodeの設定ができることは説明済みですが、作成されるmain.jsの中身を確認します。

mode=developmentで実行した場合のmain.jsファイルは下記のようになります。(一部の抜粋)


/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {

mode=productionで実行した場合のmain.jsファイルは下記のようになります。(一部の抜粋)


!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].

ファイルを見ただけで違いが明確でproductinoでは余分の空白が削除され圧縮されていることが確認できます。modeを変えることによる違いを確認することができました。

Chart.jsライブラリを使う

ここまでは、単純な自作のjavascriptファイルのみを扱ってきましたが、今度はChart.jsライブラリをwebpackで読み込んで使えるのか確認しておきます。

npmでchart.jsライブラリをインストールします。


test $ npm install chart.js
npm WARN test@1.0.0 No description
npm WARN test@1.0.0 No repository field.

+ chart.js@2.8.0
added 5 packages from 7 contributors and audited 5237 packages in 5.148s
found 0 vulnerabilities

index.htmlでチャートを表示するためのcanvasタグを入れます。


<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>Chart.jsを使ってみる</title>
	<script src="dist/main.js"></script>
</head>
<body>

	<canvas id="myChart" width="400" height="400"></canvas>
	
</body>
</html>

index.jsではインストールしたchart.jsをインポートしてバーチャートを表示させるコードを追加しています。コードはchart.jsのサイトのサンプルを利用しています。


import Chart from 'chart.js';

window.onload=function(){
var ctx = document.getElementById('myChart').getContext('2d');
	var myChart = new Chart(ctx, {
	    type: 'bar',
	    data: {
	        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
	        datasets: [{
	            label: '# of Votes',
	            data: [12, 19, 3, 5, 2, 3],
	            backgroundColor: [
	                'rgba(255, 99, 132, 0.2)',
	                'rgba(54, 162, 235, 0.2)',
	                'rgba(255, 206, 86, 0.2)',
	                'rgba(75, 192, 192, 0.2)',
	                'rgba(153, 102, 255, 0.2)',
	                'rgba(255, 159, 64, 0.2)'
	            ],
	            borderColor: [
	                'rgba(255, 99, 132, 1)',
	                'rgba(54, 162, 235, 1)',
	                'rgba(255, 206, 86, 1)',
	                'rgba(75, 192, 192, 1)',
	                'rgba(153, 102, 255, 1)',
	                'rgba(255, 159, 64, 1)'
	            ],
	            borderWidth: 1
	        }]
	    },
	    options: {
	        scales: {
	            yAxes: [{
	                ticks: {
	                    beginAtZero: true
	                }
	            }]
	        }
	    }
	});
}

ブラウザで確認するとバーチャートが表示されます。

chartjsでバーチャートを表示
chartjsでバーチャートを表示

他のライブラリを使いたい場合も同様の方法で行うことができます。

まとめ

本文書の一覧の動作確認を通してwebpackの基本設定と複数のファイルを束ねることができるということを理解することができました。冒頭でも説明した通りwebpackにはファイルを束ねる以外にさまざまな機能を持っています。その機能については下記の文書で公開しているのでさらにwebpackを理解したい場合は確認してみてください。