LOG

webpack まとめ

複数のファイルをまとめて出力するwebpack。
出力先は1にも複数にも出来るし、js以外にもcssやhtmlをまとめたり、画像データすらまとめることができ大変便利。
故にその設定は複雑で難解であり、出来上がった設定ファイルは宛ら小宇宙。

この記事はそんな小宇宙を旅するための備忘録。


下準備

外部設定ファイルの用意

設定はCLIのオプションで指定することも出来るが往々にして設定が複雑になるので外部の設定ファイルに記述した方が良い。

設定ファイルはデフォルトだと「webpack.config.js」か「webpackfile.js」になるがCLIの「–config」で変更することができる。

$ webpack --config my-config.js

NODE_ENVの設定をする

開発時と出荷時で出力内容を変えたい場合がある。
例えば、出荷時には圧縮をかけたい。パスの出力先を変更したいなど。

設定ファイルを開発用、出荷用に分けて用意する方法でも良いが環境変数NODE_ENVを利用するという手もある。

環境変数NODE_ENVはよく使われている環境変数で「production」か「development」を指定すると良い。
一部のプラグインはこの環境変数の値を基に出力を変更している。

$ NODE_ENV=production webpack --config my-config.js

また、ランタイム時に環境変数にはprocess.env.xxxでアクセスできる。
具体的には設定ファイルの中で

...
if(process.env.NODE_ENV === 'production'){
    ...
}
...

という具合に環境変数の値によって処理を変えたりする。

※ CLIのオプションに「-d」や「-p」があるが、ここでは割愛。

スクリプトを登録しておく

逐一CLIにコマンドを書くのはとても大変なのでpackage.jsonの「scripts」フィールドにコマンドを登録しておく

// package.json
{
    ...,
    scripts : {
        product : "NODE_ENV=prodcution webpack --config my-config.js"
    }
    ...
}

scriptsに登録されたコマンドは

$ npm run product

で実行できるが、いくつか最初から決められた値もあり、その決められた値で定義するとより完結に書くことができる。

// package.json
{
    ...,
    scripts : {
        start   : "NODE_ENV=development webpack --config dev.my-config.js",
        product : "NODE_ENV=prodcution webpack --config my-config.js"
    }
    ...
}

この場合、startは

$ npm start

で実行することができる。
最初から決められた値にどういったものがあるかはここを参照
npm-scripts

設定ファイルの書き方

設定された値はCommonJSのモジュール方式で渡す。
つまり設定ファイルには

module.exports = {
    context : path.join(__dirname, 'src'),
    entry   : './app.js'
    ...,
    ...
}

という具合に記述する。

用語

以下、場合によっては馴染みのない単語が出てくると思うので用語の意味を記載する。

用語
解決 (resolve)使えるようにする的な意味合い
エントリーポイント (entry point)プログラムの実行を開始する場所
チャンク (chunk)データの塊
バンドル (bundle)まとめられたファイルなど
モジュール (module)取り外し可能な部品

モジュールに関して

プログラムにおいてはモジュールは機能ごとのチャンクを指すことが多い。
webpackにおいては1つの機能であったり、1つのスタイルであったりするわけで
1つのファイルという認識で良いと思う。
action.jsやpicture.jpgなど。

webpackではモジュールの呼び出し(依存関係の表現)に対して以下の方法を取ることができる。

  • ES2015のimport
  • CommonJSのrequire()
  • AMDのdefineとrequire
  • CSSの@import
  • CSSの画像を読み込むurlとかhtml

webpackはmodule bundlerである。
つまりモジュール単位に分割された様々なファイルをバンドルするツール。

ローダーに関して

webpackがcssファイルの扱い方やjpgデータをデータURLに変換する方法を知っているわけではない。
webpackは、なんでもは知らない。知っていることだけ。

なので、webpackが知っている形にモジュールを変換する必要がある。
その変換するものをローダー(loader)と言い、これらがwebpackのビルド処理の前に実行されることによって
様々なファイルをバンドルすること可能にしている。


webpack説明

webpackにおける設定は大きく分けて

  • 入力ファイルの設定
  • 出力ファイルの設定
  • モジュールの設定
  • サードパーティの設定 (pluginなど)
  • その他の動作に関する設定 (contextやalias,watchなど)

に分けれると思う。
このうち、入力ファイルの設定と出力ファイルの設定は必須だがあとはオプションとなる。

以下の説明ではこのような構成を想定して説明をする。

├ asset 素材ファイルが入るディレクトリ
│   ├ js
│   ├ sass
│   └ img
├ dest // バンドル化されたファイルが入るディレクトリ
├ package.json
├ webpack.config.js
└ index.html

context

エントリーポイントを解決するためのルートディレクトリ
省略時は実行時のディレクトリになる。

大体は入力ファイルが入っているディレクトリを指定する。

{
    context : path.join(__dirname, 'asset')
}

output

出力ファイルの管理を行う。
outputフィールドは以下で説明するもの以外にも色々ある。
webpack documentation OUTPUT

path

出力ファイルを格納するディレクトリを返す。
絶対パスで記載する。
省略時は実行時のディレクトリ。

{
    output : {
        path : path.join(__dirname, 'dest')
    }
}

publicPath

publicPathは本番用などに利用するパス。
主にプラグインなどで利用される。

{
    output : {
        publicPath : '/dest/'
    }
}

参考
webpack guide Public Path

filename

出力ファイル名前を指定する。

{
    output : {
        filename : 'bundle.js'
    }
}

複数出力する場合は[XXX]の形で固有の値を指定することができる。
指定できるタイプは以下の通り。

[name]チャンクの名前に置換
[hash]コンパイラのハッシュに置換
[chunkhash]チャンクのハッシュに置換
{
    output : {
        filename : '[name].js'
    }
}

entry

入力ファイルの管理を行う。
エントリーの指定方法は複数あり

  • オブジェクト
  • 配列
  • 文字列

の3種類が存在する。

オブジェクトの場合

おそらくこの方法を最も冗長的でよく使う。

{
    context : path.join(__dirname, 'asset'),
    entry : {
        'a'       : './a.js',
        'child/b' : './b.js'
    },
    output  : {
        path     : path.join(__dirname, 'dest/js'),
        filename : '[name].js'
    }
}

この場合「dest/js/a.js」と「dest/js/child/b.js」
が出力される。

配列の場合

お互いに依存しないファイル群を1つにまとめたりするのに利用する。

{
  entry : [
    './js/lib/a.js',
    './js/lib/b.js',
    ['./js/lib/c.js', './js/lib/d.js']
  ]
}

文字列の場合

文字列は単純に以下の糖衣構文である。

{
  entry : {
    main : './path/to/my/entry/file.js'
  }
}

module

moduleはファイルをどのように扱うかを決める。
moduleではRuleという列挙体で条件を追加していく。

おそらくここが一番ややこしい。

module.rulesにRuleを入れることで複数追加することができる。

{
    module : {
        rules : [
            { // これがRule
                test : /\.html$/,
                ...
            }
        ]
    }
}

moduleで定義している設定一覧はここを参照
webpack documentation module

Ruleの構成

Rule
  ├ use           - 結果 or ネスト | useEntry
  │  ├ options   - 結果
  │  └ loader    - 結果
  ├ resource
  │  ├ include   - 条件
  │  ├ exclude   - 条件
  │  └ test      - 条件
  ├ resourceQuery - 条件
  ├ issuer        - 条件
  ├ enforce       - 結果
  ├ parser        - 結果
  └ oneOf         - 結果 or ネスト

Ruleは「条件」「結果」「ネスト」の3つの部分に分けることができる。

Rule : ショートカット

ショートカット用のフィールドがある。

Rule
  ├ loader  - ショートカット-> Rule.use.loader
  ├ query   - ショートカット-> Rule.use.options
  ├ options - ショートカット-> Rule.use.options
  ├ include - ショートカット-> Rule.resource.include
  ├ exclude - ショートカット-> Rule.resource.exclude
  └ test    - ショートカット-> Rule.resource.test

Rule : 条件

条件は次のいずれかになる。

  • ディレクトリかファイルまでの絶対パスの文字列
  • 正規表現
  • 真偽値を返す関数
  • 全ての動作が定義されたプロパティが一致したオブジェクト
test正規表現かその配列
include文字列かその配列
exclude文字列かその配列

※ note:ドキュメントにはand,or,notがあるがこれがどれを指すのかがわかっていない。

Rule : 結果

結果は条件にマッチした場合にのみ適用されるもの。

Rule : ネスト

useとoneOfは配列を渡すことでRuleのネストが可能になっている。

{
    context : path.join(__dirname, 'assets'),
    entry   : './app.js',
    output  : {
        filename : "yokota.js",
        path     : path.join(__dirname, 'dest')
    },
    module : {
        rules : [
            {
                resource : { test : /\.sass$/ },
                use : [
                    {
                        loader : 'css-loader'
                    },
                    {
                        loader : 'sass-loader'
                    }
                ]
            }
        ]
    }
}

この場合、Sassファイルに対してsass-loaderが実行され、cssに変換し
css-loaderが実行されcssがwebpackでも読めるチャンクへと変換される。

複数していした場合は下から評価される。

plugin

loaderがバンドル前のモジュール(ファイル)に対して適用させるように
pluginはバンドルされたファイルに対して適用させる。

例えば、ファイルの圧縮をしたり、ファイル内部のパスを置換したり。

プラグイン一覧でどういったものがあるか確認できる。
プラグイン一覧


まとめ

要約すると、

  • webpackはjs以外にも様々なファイルをバンドルすることができる。
  • js以外のファイルをwebpackに認識させるためにはloaderを利用する。
  • loaderはmoduleのuseEntry列挙体を使い指定する。
  • バンドルに適用させたい操作はpluginを利用する。