Chapter 03無料公開

webpack を利用してみる

soarflat
soarflat
2020.09.26に更新

本章では、webpack を最小構成で利用できる環境を構築し、モジュールをバンドルしたファイルを出力してみます。

モジュールのバンドルと出力のイメージは次の通りです。

この章を一通り読んで実践していただくことで、webpack を利用した開発のイメージを掴んでいただくことができるはずです。

この章で利用しているサンプルコードはこちらになります。

webpack-practical-book/chapter03/01-getting-started-webpack/

3.1 事前準備

webpack を利用するためには Node.js が必要ですので、導入していない方は公式サイトからインストールしてください。

https://nodejs.org/ja/

インストールが成功すれば、コマンドラインで npm コマンドを利用できるようになります。

npm -v
# 以下のようにバージョンが出力される
6.14.8

3.2 ディレクトリとファイル構成

任意の場所にgetting-started-webpackという名前の作業用ディレクトリを作成してください。そこで作業を行なっていきます。

今回の手順に沿ってファイルを出力した場合、最終的なディレクトリとファイル構成は次のようになります(出力されるファイルも含む)。

getting-started-webpack
├── node_modules
├── package.json
├── package-lock.json
├── public
│   ├── index.html
│   └── js
│       └── bundle.js
├── src
│   └── js
│       ├── app.js
│       └── modules
│           └── math.js
└── webpack.config.js

ファイルを出力するまでの手順は次の通りです。

  1. プロジェクトの設定情報が記述されたpackage.jsonファイルを生成する
  2. webpack をインストールする
  3. 外部モジュール(サードパーティのモジュール)をインストールする
  4. webpack を npm scripts で実行できるようにする
  5. webpack の設定ファイル(webpack.config.js)を作成する
  6. モジュールを作成する
  7. エントリーポイントを作成し、モジュールを読み込む
  8. webpack を実行してモジュールをバンドルしたファイルを出力する
  9. 出力したファイルが動作しているか確認

3.3 プロジェクトの設定情報が記述された package.json ファイルを生成する

まずはコマンドラインでgetting-started-webpackディレクトリに移動します。

cd ~/getting-started-webpack

そして、次のコマンドを実行すれば、package.jsonファイルが作成されます。

npm init -y

生成される package.json は次の通りです。

{
  // アプリの名前
  "name": "getting-started-webpack",
  // アプリのバージョン
  "version": "1.0.0",
  // アプリの説明
  "description": "",
  // モジュールの中で最初に呼ばれるスクリプト
  "main": "index.js",
  // npm scripts
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  // キーワード
  "keywords": [],
  // 著者情報
  "author": "",
  // ライセンス情報
  "license": "ISC"
}

今回は不要な記述もあるため、それらを削除して次のようにします。

{
  "name": "getting-started-webpack",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

3.4 webpack をインストールする

webpack を利用するためには、webpackwebpack-cliというパッケージが必要ですので、それらをインストールします。

次のコマンドで、webpackwebpack-cliをインストールします。

npm install --save-dev webpack@4.44.2 webpack-cli@3.3.12

インストールが成功すれば、package-lock.jsonnode_modulesディレクトリが生成されます。

インストールしたwebpackwebpack-clinode_modulesに保存されています。

また、package.jsondevDependenciesフィールドとインストールしたパッケージ情報が追加されます。

{
  "name": "getting-started-webpack",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "devDependencies": {
    "webpack": "^4.44.2",
    "webpack-cli": "^3.3.12"
  }
}

3.5 外部モジュール(サードパーティのモジュール)をインストールする

今回、外部モジュールはjqueryを利用します。

次のコマンドを実行すればjqueryがインストールされます。

npm install --save jquery@3.5.1

インストールが成功すれば、package.jsondependenciesフィールドとインストールしたパッケージ情報が追加されます。

{
  "name": "getting-started-webpack",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "devDependencies": {
    "webpack": "^4.44.2",
    "webpack-cli": "^3.3.12"
  },
  "dependencies": {
    "jquery": "^3.5.1"
  }
}

3.5.1 devDependencies と dependencies に関して

上記の通り、npm install --save-devでインストールしたパッケージはdevDependenciesフィールドに追加されます。

そして、npm install --saveでインストールしたパッケージはdependenciesフィールドに追加されます。

devDependenciesは開発時にのみ必要なパッケージでdependenciesは実行時に必要なパッケージであることを示しています。

今回の場合、webpackwebpack-cliはファイルを出力するためだけに利用するので、devDependenciesです。

一方で、jqueryは出力されるファイルにバンドルされるため、dependenciesです。

要は、出力するファイルはjqueryがないと動作しないことを示しています。

もし、jquerydevDependenciesの方にあったら問題あるのかと言うと、このプロジェクトは npm に公開するわけではないので特に問題ありません。

しかし、今回のようにdependenciesdevDependenciesが分けられていた方が、自分以外の開発者がこの開発環境を見た時に助かります。

ちゃんと分けられていれば、どのパッケージが開発用もしくは実行ファイルに必要なのかが理解しやすいからです。

そのため、普段からdependenciesdevDependenciesがちゃんと分かれるように、--save--save-devを使い分けることをおすすめいたします。

3.6 webpack を npm scripts で実行できるようにする

webpack はwebpackコマンドで実行できますが、実際の開発では npm scripts(コマンドを別名で実行できるショートカットのような仕組み)を利用して実行することが一般的です。

npm scripts を利用するためには、package.jsonscriptsフィールドにwebpackコマンドを追加します。

{
  "name": "getting-started-webpack",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^4.44.2",
    "webpack-cli": "^3.3.12"
  },
  "dependencies": {
    "jquery": "^3.5.1"
  }
}

これで、npm run buildコマンドで webpack を実行できるようになりました。

今回はwebpackコマンドだけを実行するようにしましたが、次のようにオプションがついたコマンドも指定できます。

{
  "scripts": {
    "dev": "webpack --mode development --watch --hide-modules"
  }
}

3.6.1 なぜ npm scripts を利用するのか

webpackコマンドをわざわざnpm run buildで実行するのは冗長だと感じたかもしれません。

確かに冗長ですが、npm scripts を利用することで、プロジェクトで利用するコマンドを明示できます。

コマンドを明示することで、開発環境を構築した以外の人でも、scriptsフィールドさえ見れば、このプロジェクトではどのようなコマンドを利用して開発を行うのかなどを理解できます。

逆に、scriptsフィールドに何も書いていないと、ビルドの方法さえわからないため、かなり困ります。

というわけで、実際の開発では npm scripts を利用することが一般的なため、普段から利用することをお勧めいたします。

3.7 webpack の設定ファイル(webpack.config.js)を作成する

エントリーポイントや出力の設定をしたいため、webpack.config.jsという設定ファイルを作成します。

今回の設定は次の通りです。

// Node.js に組み込まれているモジュール。出力先などの指定をするために利用する。
const path = require('path');

module.exports = {
  // モード
  mode: 'development',
  // エントリーポイント
  entry: './src/js/app.js',
  // 出力の設定
  output: {
    // 出力先のパス(絶対パスを指定しないとエラーが出るので注意)
    // `__dirname + public`のように書くと、OS によって
    // パスが異なってしまうことがあるので、必ず`path`モジュールを利用する。
    path: path.resolve(__dirname, 'public'),
    // 出力するファイル名
    filename: 'js/bundle.js',
  },
};

設定の各項目の詳細は次の通りです。

3.7.1 mode

webpack のモードを指定します。

'development''production''none'のいずれかのモードを指定します。

選択したモードによって webpack の動作が異なります。

'development'にすれば、ソースマップを生成したり再ビルド時間を短縮するなど設定が有効になるため、開発時にはこちらのモードを利用します。

また、'production'にすれば、ファイルの圧縮や、モジュールの最適化などの設定が有効になるため、本番用のファイルの出力にはこちらのモードを利用します。

今回は開発用のモードである 'development'を指定します。

モードを指定しないと webpack を実行時に警告が出るため、必ず指定してください。

3.7.2 entry

エントリーポイントを指定します。

今回指定している./src/js/app.jsは後ほど追加します。

3.7.3 output

ファイルの出力先や出力するファイル名など、出力に関する指定をします。

output.pathには絶対パスを指定をしないとエラーが発生するため、pathモジュールを利用して絶対パスを指定してください。

今回の設定だとpublic/jsディレクトリにbundle.jsが出力されます。

3.8 モジュールを作成する

src/js/modules/math.jsというモジュールを作成し、2つの関数を定義します。

今回、これらの関数をエントリーポイントで利用できるようにしたいので、次のようにfunctionの前にexport文を記述します。

export function add(number1, number2) {
  return number1 + number2;
}

export function subtract(number1, number2) {
  return number1 - number2;
}

このように、export文で外部から利用できるようにした関数やオブジェクトなどのことを「エクスポート」と言います。

3.9 エントリーポイントを作成し、モジュールを読み込む

jquerysrc/js/modules/math.jsをインポートして利用するsrc/js/app.jsというエントリーポイントを作成します。

モジュールを読み込むにはimport文を利用します。

import $ from 'jquery';
import { add, subtract } from './modules/math';

const item1Price = 400;
const item2Price = 600;
const coupon = 300;
const totalPrice = add(item1Price, item2Price);
const priceAfterApplyCoupon = subtract(totalPrice, coupon);

$('body').text(priceAfterApplyCoupon);

3.9.1 モジュールをインポートする記述に関して

上記のコードでは、外部モジュールであるjqueryをモジュール名だけを指定してインポートしています。

import $ from 'jquery';

モジュール名だけを指定すると、webpack はnode_modules配下のモジュールを自動で探しに行き、該当するモジュールがあればそれを利用します。

そのため、次のように相対パスを指定する必要はありません。

import $ from '../../node_modules/jquery/dist/jquery';

そして、自作のモジュールであるsrc/js/modules/math.jsは相対パスを指定してインポートしています。

import { add, subtract } from './modules/math';

拡張子(.js)を書いていませんが、webpack が.jsがつくファイルを探しに行ってくれます。

そのため、次のように拡張子を指定する必要はありません(.tsファイルの場合、設定を変更する必要があります)。

import { add, subtract } from './modules/math.js';

3.9.2 export文とimport文の様々な書き方

今回、export文とimport文でモジュールを扱っていますが、これは ES Modules という仕様に沿った書き方です。

他にもモジュールを扱える形式はありますが、今日では ES Modules の形式でモジュールを扱うのが一般的です。

export文とimport文には様々な書き方があります。

例えば、次のようなモジュールmath.jsがあるとします。

export const add = (n, n2) => n + n2;
export const sub = (n, n2) => n - n2;

このようなモジュールは、次のように様々な構文でインポートして利用できます。

// モジュールから add だけ読み込む
import { add } from './math.js';
add(1, 2); // -> 3

// モジュールから add を読み込み、 sub を SUB という名前で読み込む
import { add, sub as SUB } from './math.js';
add(1, 2); // -> 3
SUB(3, 2); // -> 1

// 全てのエクスポートをモジュールから読み込む
import * as math from './math.js';
math.add(1, 2); // -> 3
math.sub(3, 2); // -> 1

exportだけではなく、次のようにexport defaultでエクスポートすることもできます。

const add = (n, n2) => n + n2;
const sub = (n, n2) => n - n2;
export default { add, sub };

上記の場合、次のようにインポートして利用できます。

import math from './math.js';
math.sub(3, 2); // -> 1
math.add(1, 2); // -> 3

また、次のようにexportexport defaultが混在する時もあります。

export const add = (n, n2) => n + n2;

const sub = (n, n2) => n - n2;
export default sub;

この場合、次のようにすればどちらも読み込めます。

import sub, { add } from './math.js';
sub(3, 2); // -> 1
add(1, 2); // -> 3

3.10 webpack を実行してモジュールをバンドルしたファイルを出力する

今回、npm scripts を利用するため、次のコマンドで webpack を実行します。

npm run build

webpack が正常に実行されれば、コマンドライン上に次のような出力がされます。

Hash: a326393c3fa4651d0e27
Version: webpack 4.44.2
Time: 179ms
Built at: 2020-09-24 0:42:00
    Asset     Size  Chunks             Chunk Names
bundle.js  323 KiB    main  [emitted]  main
Entrypoint main = bundle.js
[./src/js/app.js] 284 bytes {main} [built]
[./src/js/modules/math.js] 146 bytes {main} [built]
    + 1 hidden module

そして、public/jsディレクトリにbundle.jsが出力されます。

3.11 出力されたファイルの動作確認

出力されたpublic/js/bundle.jsが動作しているか確認するために、それを読み込むpublic/index.htmlを作成し、ブラウザで開きます。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>getting started webpack</title>
  </head>
  <body>
    <script src="js/bundle.js"></script>
  </body>
</html>

次のように計算結果が描画されているため、正常に動作していることがわかります。

というわけで、webpack を利用してモジュールをバンドルしたファイルを出力できました。

今回ご紹介したのが webpack の最小構成での利用方法のため、まずはここから理解していきましょう。

webpack を使いこなすためには他にも様々な機能を理解する必要があるため、次章以降ではそれらをご紹介していきます。