LoginSignup
25
29

More than 1 year has passed since last update.

【Webpack】webpack.config.jsの内容がいまいちわからない方々へ

Last updated at Posted at 2021-08-14

はじめに

Webpackは、JavaScript, CSS, JPGなどのリソースを一つのjsファイルにバンドルする(まとめる)ツールです。
バンドルすることで、各リソースの依存関係を気にする必要がなくなり、不適切な順序でファイルが実行されるような心配がなくなります。

モダンなJS開発でほぼ必須のWebpackですが、設定ファイルwebpack.config.jsの中身についていまいちよくわかっていませんでした。
設定ファイルを自由にカスタムできるようになれば開発の幅も広がる気がしたので、以下の基本項目について学びなおしました。

  • Entry & Output
  • Asset Modules
  • Loaders
  • Plugins
  • Mode

Entry & Output

バンドル設定の手始めとして、entryに入力ファイルのエントリーポイント、outputにバンドル後の出力ファイル名filenameと作成パスpathを指定してあげる必要があります。
作成パスは絶対パスで記述する必要があるため、Node.jsのpathモジュールを使い、path.resolve(__dirname, './dist')のようにします。

webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/index.js', // 入力ファイルのエントリーポイント
  output: {
    filename: 'bundle.js', // 出力ファイル名
    path: path.resolve(__dirname, './dist'), // 出力ファイルのパス
  },
  mode: 'none',
};

Asset Modules

AssetはWebpack5から追加されたモジュールです。
指定した形式のアセット(txt, jpgなどの静的ファイル)について、バンドルの仕方(バンドルファイル内にまとめるのか、切り離すのか、など)を以下の4つの方法で指定することができます。

  • asset/resource
  • asset/inline
  • asset
  • asset/source

asset/resource

asset/resourcemodulerulesで指定すると、ビルド時にリソース(以下の場合だとpngjpg)がbundle.jsとは別に出力されます。
スクリーンショット 2021-08-09 15.04.11.png

webpack.config.js
const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist')
    },
    mode: 'none',
    module: {
        rules: [
            {
                test: /\.(png|jpg)$/,
                type: 'asset/resource'
            }
        ]
    }
};

asset/inline

asset/resourceだと指定した形式のリソースがbundle.jsとは別に出力されるため、画像がたくさんある場合などについては、読み込む際に都度HTTPリクエストが飛ぶことになります。

asset/inlineを利用すると、リソースはbase64形式でbundle.jsにひとまとめにされます。
SVGなどのサイズが小さいファイルがたくさんある場合など、ひとまとめにした方が通信コストを抑えることができるときにasset/inlineを指定します。

asset

assetを指定すると、バンドルするファイルサイズが8kB以上だとasset/resource、8kB以下だとasset/inlineと同様の扱いになります。
この閾値は以下のように変更することができます。

module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 3 * 1024, // 3kB
          },
        },
      },
    ],
  },

asset/source

asset/sourceでは読み込んだファイルをJavaScriptのソースコードとして扱います。

      {
        test: /\.txt/,
        type: 'asset/source',
      },

Loaders

LoadersはAsset Modulesで取り扱えないリソース(SASS, CSS, XMLなど)をインポートするときに使用します。

CSS

CSSファイルをバンドルするためには、css-loaderstyle-loaderが必要です。
css-loaderはCSSをJSで扱える形に変換するloaderで、style-loaderはHTMLのheadタグ内にインラインスタイルとしてCSSを挿入するloaderです。

npm install css-loader style-loader --save-dev
            {
                test: /\.css$/,
                use: [
                    'style-loader', 'css-loader'
                ]
            }

SASS

SASSをバンドルするためには、SASSをCSSに変換するsass-loaderを追記します。

            {
                test: /\.scss$/,
                use: [
                    'style-loader', 'css-loader', 'sass-loader'
                ]
            }

Babel

babel-loaderでJavaScriptファイルを下位のES仕様にトランスパイルすることができます。
例えば、ES6のコード(クラス構文、アロー関数、async/awaitなど)はIEで動かないため、このような変換が必要となります。

以下のモジュールをインストールし、babel-loaderの設定を行います。

npm install @babel/core babel-loader @babel/preset-env @babel/plugin-proposal-class-properties --save-dev

presets: [ '@babel/env' ]でES5へトランスパイルします。
ES6のクラス構文をトランスパイルするために、plugins: [ '@babel/plugin-proposal-class-properties' ]というプラグインが別途必要になります。

            {
                test: /\.js$/,
                exclude: /node_modules/, //node_modules以下のファイルは対象から除く
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [ '@babel/env' ], // ES5に変換
                        plugins: [ '@babel/plugin-proposal-class-properties' ] // クラス構文を変換可能にするプラグイン
                    }
                }
            }

Plugins

Plugins(プラグイン)はLoadersでできないことを実現するためのJSライブラリです。
プラグインの種類によってはバンドルファイルのサイズを減らしたりすることもできます。

terser-webpack-plugin

terser-webpack-pluginはバンドルファイルのサイズを小さくするためのプラグインです。
以下のように記述します。

const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist'),
        publicPath: 'dist/'
    },
    mode: 'none',
    module: {
        rules: [
          // 省略
        ]
    },
    plugins: [
        new TerserPlugin()
    ]
};

mini-css-extract-plugin

バンドルファイルのサイズが大きくなってしまったときに、CSSファイルのみを分離するためのプラグインがmini-css-extract-pluginです。
rulesに記述したstyle-loaderMiniCssExtractPlugin.loaderに置き換えます。

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    // 省略
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader, 'css-loader'
                ]
            },
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'
                ]
            },
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'styles.css'
        })
    ]
};

また、CSSファイルが分離されるので、htmlのheadタグに<link rel="stylesheet" href="./dist/styles.css" />を忘れずに追加します。

clean-webpack-plugin

Webpackでbundle.jsにひとまとめにすると、ブラウザキャッシュによって、コードに変更を加えても画面に反映されないケースがあります。
そんなときは、出力ファイルをfilename: 'bundle.[contenthash].js'とすることによって、ビルドするたびに新しい名前でバンドルファイルが出力されるようにします。

ただ、このままだとビルドを繰り返していくうちにバンドルファイルが溜まってしまいます。
必要なファイル以外を削除するために使用するのがclean-webpack-pluginです。

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, './dist'),
        publicPath: ''
    },
    plugins: [
        new CleanWebpackPlugin()
    ]
};

html-webpack-plugin

contenthashでビルドするたびに新しいバンドルファイルが生成されると、htmlで読み込む際にもファイル名を書き換えなければなりません。
それだと面倒なので、html-webpack-pluginを使用して、正しい読込ファイルが記述されたhtmlファイルも同時に生成します。

htmlファイルはバンドルファイルと共にdistフォルダに出力されるので、publicPath: ''とします。

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, './dist'),
        publicPath: ''
    },
    plugins: [
        new HtmlWebpackPlugin()
    ]
};

スクリーンショット 2021-08-10 22.21.44.png

また、出力するhtmlの内容を以下のようにカスタマイズすることもできます。

        new HtmlWebpackPlugin({
            title: 'Hello world',
            meta: {
                description: 'Some description'
            }
        })

handlebarsでhtmlのテンプレートを用意して出力する方法もあります。

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    module: {
        rules: [
            {
                test: /\.hbs$/,
                use: [
                    'handlebars-loader'
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: 'Hello world',
            template: 'src/index.hbs',
            description: 'Some description'
        })
    ]
};
index.hbs
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{{htmlWebpackPlugin.options.title}}</title>
        <meta name="description" content="{{htmlWebpackPlugin.options.description}}">
    </head>
<body>
</body>
</html>

Mode

ビルドにはproductionとdevelopmentの2つのモードがあります。
productionでビルドしたファイルはできるだけ小サイズで最適化されるようにし、developmentの場合はビルドの時間ができるだけ短いようにしてあげる必要があります。

configファイルをwebpack.dev.config.js webpack.production.config.jsのように別々に作成します。
例えば、webpack.dev.config.jsでは、ビルドができるだけ早くなるように、contenthashやMiniCssExtractPluginを使用しません。
devServerでwebpack-dev-serverの設定も行っています。

webpack.dev.config.js
module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js', // contenthashは使用しない
        path: path.resolve(__dirname, './dist'),
        publicPath: ''
    },
    mode: 'development', // development
    devServer: {
        contentBase: path.resolve(__dirname, './dist'),
        index: 'index.html',
        port: 9000
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader', 'css-loader' // MiniCssExtractPluginを使用しない
                ]
            },
        ]
    },
};

ビルドの実行コマンドは以下のようにします。
devの方については、ビルドと同時にwebpack-dev-serverが起動するようになっています。
--hotオブションをつけることで、コード修正を行うたびにビルドが行われ、ブラウザに反映されるようになります。

package.json
  "scripts": {
    "build": "webpack --config webpack.production.config.js",
    "dev": "webpack serve --config webpack.dev.config.js --hot"
  },

参考資料

25
29
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
29