webpack 4とmini-css-extract-pluginを使ってCSSを別ファイルに書き出す方法

UI開発者 古川

Vue.jsで単一ファイルコンポーネントを用いる際にCSSをひとつのファイルに書き出す方法は公式のドキュメントに掲載されています。しかしドキュメント内で紹介されているExtract Text Pluginの最新バージョン(v3.0.2)は2018年12月現在webpack 4に未対応なため、webpack 4の環境下で実際に導入するとエラーが出てしまいます。

そこで今回は、Extract Text Pluginで利用を推奨されているmini-css-extract-pluginを用いてVue.jsの単一ファイルコンポーネントに記述されているCSSを別のファイルに書き出す方法をご紹介します。

mini-css-extract-pluginの導入

webpack 4で単一コンポーネントを用いる準備が最小限整っている段階から始めます。

webpack.config.jsの設定は以下のコードです。

const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: `${__dirname}/dist`
  },
  module: {
    rules: [{
      test: /\.vue$/,
      loader: 'vue-loader'
    },
    {
      test: /\.(sc|c|sa)ss$/,
      use: [
        'vue-style-loader',
        'css-loader',
        'sass-loader',
      ]
    }]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

現段階で実行したファイルの状態が以下のキャプチャです。現段階でCSSは書き出されていません。

さっそく環境を整えていきましょう。まずmini-css-extract-pluginをダウンロードしたのち、webpack.config.jsを以下に変更します。

const VueLoaderPlugin = require('vue-loader/lib/plugin');
// mini-css-extract-pluginを読み込み
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: `${__dirname}/dist`
  },
  module: {
    rules: [{
      test: /\.vue$/,
      loader: 'vue-loader'
    },
    {
      test: /\.(sc|c|sa)ss$/,
      use: [
        // vue-style-loaderをMiniCssExtractPlugin.loaderに変更
        MiniCssExtractPlugin.loader,
        'css-loader',
        'sass-loader',
      ]
    }]
  },
  plugins: [
    new VueLoaderPlugin(),
    // MiniCssExtractPluginのインスタンスを追記
    // ここで設定するfilenameは出力するファイル名
    new MiniCssExtractPlugin({
      filename: 'assets/css/app.css'
    })
  ]
}

ローダーの設定順がポイントです。ローダーは記述順の後ろから前に実行されるため、

  1. sass-loaderでSassをCSSにコンパイル
  2. css-loaderでCSSの依存関係を解決
  3. mini-css-extract-plugin.loaderでCSSファイルの書き出し

という処理順を実行したい場合、上記のような記述となります。

この段階で実行した状態が以下のキャプチャです。CSSが書き出されています。

開発版とビルド版の出し分け

CSSファイルをひとつにまとめて出力する環境でwebpack-dev-serverを導入する場合は注意が必要です。

webpack-dev-serverはデフォルトではメモリ上のファイルを参照するのでバンドルしたファイルを出力しません。そのためwebpack-dev-serverを実行している状態でmini-css-extract-pluginを動作させても、ブラウザ上では出力された単一のCSSが読み込みされている状態が実現されますが実際にCSSファイルは出力されません。

上記の点を回避するために、Node.jsの環境変数を利用してdevelopmentモードの際はvue-style-loaderを用いてCSSをhead内で読み込み、productionモードの際はmini-css-extract-pluginでCSSを出力するよう設定します。

今回は環境変数の利用においてプラットフォーム間の環境変数の記述の差分を吸収するcloss-envを導入します。closs-envをダウンロードしたあと、package.jsonを以下のように記述します。

"scripts": {
  "dev": "cross-env NODE_ENV=development webpack-dev-server",
  "build": "cross-env NODE_ENV=production webpack"
},

続いて環境変数に応じてwebpackのモードや読み込むローダーを切り替えるため、webpack.config.jsを以下のように設定します。

const VueLoaderPlugin = require('vue-loader/lib/plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 環境変数に応じて判定
const isDevelopment = process.env.NODE_ENV === 'development';

module.exports = {
  // 判定に応じてモードを切り替え
  mode: isDevelopment ? 'development': 'production',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: `${__dirname}/dist`
  },
  module: {
    rules: [{
      test: /\.vue$/,
      loader: 'vue-loader'
    },
    {
      test: /\.(sc|c|sa)ss$/,
      use: [
        // 判定に応じて実行するローダーを切り替え
        isDevelopment ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
        'css-loader',
        'sass-loader',
      ]
    }]
  },
  // webpack-dev-serverの導入
  devServer: {
    contentBase: 'dist',
    port: 3000
  },
  plugins: [
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: 'assets/css/app.css'
    })
  ]
}

まとめ

extract-text-webpack-plugin v4.0.0 betaを使用すれば現状エラーが発生しませんが、次期バージョンの導入に不安を覚える方はmini-css-extract-pluginの導入を検討してみてはいかがでしょうか。