webpack@4 でCSSを抽出する際は mini-css-extract-plugin を使う

この記事を読んでいて知ったのだが、CSSを抽出する目的でextract-text-webpack-pluginを使うのは非推奨になったらしい。
qiita.com

github.com

webpack@4では mini-css-extract-plugin というプラグインを使うことが推奨されている。
というわけで、その使い方を書いていく。

webpack でのCSSの扱いの基礎については、以前書いたこの記事を参照。
numb86-tech.hatenablog.com

本記事は以下のバージョンで動作確認している。

  • webpack@4.20.2
  • webpack-cli@3.1.2
  • css-loader@1.0.0
  • mini-css-extract-plugin@0.4.4
  • optimize-css-assets-webpack-plugin@5.0.1
  • terser-webpack-plugin@1.1.0

まずはCSSを webpack の対象にする

復習を兼ねて、動作確認用の環境を一から作っていく。

まずはwebpackwebpack-cliをインストールする。

$ npm i -D webpack webpack-cli

webpack でCSSを扱えるようにするためにcss-loaderもインストールする。

$ npm i -D css-loader

今回はsrc/index.jsというファイルでsrc/style.cssを読み込んで使う。

// src/index.js
import './style.css';

const body = document.querySelector('body');
body.innerHTML += 'Hello World!';
/* src/style.css */
body {
  color: red;
}

最後にwebpack.config.jpを作成する。

// webpack.config.jp
const path = require('path');

module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dest'),
  },
  module: {
    rules: [
      {test: /\.css$/, use: ['css-loader']}
    ],
  },
}

この状態で$ npx webpack --mode developmentを実行すると、dest/main.jsが生成される。
dest/main.jsには、src/index.jssrc/style.cssの内容が含まれている。

だがこのままでは、CSSの内容がアプリに反映されることはない。

CSSを実際に使えるようにするための方法の一つとして、extract-text-webpack-pluginがあった。
これは、webpack で出力した結果をテキストとしてファイルに抽出するプラグインで、これを使うことでCSSファイルを出力することが出来た。
webpack@4では、extract-text-webpack-pluginの代わりにmini-css-extract-pluginを使用する。

mini-css-extract-plugin の基本的な使い方

まずはインストールする。

$ npm i -D mini-css-extract-plugin

webpack.config.jpを以下のように書き換える。

// webpack.config.jp
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dest'),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({filename: 'style/[name].css'}),
  ],
}

これで$ npx webpack --mode developmentすると、以下の内容のdest/style/main.cssが出力される。

body {
  color: red;
}

また、dest/main.jsも更新され、src/style.cssの内容が省かれている。

あとはHTMLファイルでdest/main.jsdest/style/main.cssを読み込めば正しく動作し、赤文字の「Hello World!」が表示される。

CSSファイルの圧縮

$ npx webpack --mode productionのようにprodcutionモードでビルドすると、JSファイルを圧縮してくれる。
先程の例だと、dest/main.jsワンライナーに圧縮される。

だがdest/style/main.cssは何も変わらない。
mini-css-extract-pluginで抽出したCSSファイルを圧縮したい場合は、プラグインを使って明示的に設定する必要がある。

まずはプラグインをインストールする。

$ npm i -D optimize-css-assets-webpack-plugin

webpack.config.jsを以下の内容にする。OptimizeCSSAssetsPluginrequireし、optimizationの記述を追加している。

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dest'),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({filename: 'style/[name].css'}),
  ],
  optimization: {
    minimizer: [new OptimizeCSSAssetsPlugin({})],
  },
}

これで$ npx webpack --mode productionすると、dest/style/main.cssワンライナーに圧縮される。

だが今度は、dest/main.jsが圧縮されていない。
実はoptimization.minimizerを記述すると圧縮の設定が上書きされてしまうので、JSについても明示的に設定しなければならない。

これまではJSの圧縮にはuglifyjs-webpack-pluginというプラグインを使っていたが、v2でES2015の対応が外れてしまった。今後ES2015+のコードを使う場合は、terser-webpack-pluginというプラグインを使う必要がある。

github.com

というわけで、terser-webpack-pluginをインストール。

$ npm i -D terser-webpack-plugin

そしてwebpack.config.jsを更新。

@@ -1,6 +1,7 @@
 const path = require('path');
 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
+const TerserPlugin = require('terser-webpack-plugin');
 
 module.exports = {
   entry: {
@@ -25,6 +26,6 @@ module.exports = {
     new MiniCssExtractPlugin({filename: 'style/[name].css'}),
   ],
   optimization: {
-    minimizer: [new OptimizeCSSAssetsPlugin({})],
+    minimizer: [new TerserPlugin({}), new OptimizeCSSAssetsPlugin({})],
   },
 }

これでビルドすると、dest/main.jsdest/style/main.cssも圧縮される。