在這之前有介紹 Webpack 基礎設定,以及搭配 Babel 設定介紹,這次我們來介紹如何在 Javascript 引入 CSS 檔案做開發。

首先我們先來練習最基礎的引入 CSS

安裝 CSS

$ npm install css-loader style-loader --save-dev
  • css-loader 幫我們把 CSS 檔案透過 @import 或是 url()的方式載入到 Javascript 內
  • style-loader 將載入好的 CSS 放置 html 讓瀏覽器可以讀取

設定 CSS

編輯 webpack.config.js 新增 module

//webpack.config.js
module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: './index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index.bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.(js)$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
};

注意:webpack 會從右向左執行,依據上面執行解析大概的意思是:當載入.css 檔案時,先用 css-loader 解析,再用 style-loader 將 CSS 加入到頁面中,順序要注意是否正確!!

執行編譯

新增 CSS 檔案

隨便打一些內容測試

index.js import CSS

$ npm run start

看看在網頁上呈現的效果

CSS 成功引入至 index.html 中

雖然大家都知道內聯 CSS 是優化的作法(把 CSS style 直接放入 head),可是維護上總是不方便,我希望 另外獨立出來一個 CSS 檔案引入,這時候可以使用 mini-css-extract-plugin

mini-css-extract-plugin

是用來將每個由 webpack 產生內含有 css 的 js 檔案轉成 css 檔

安裝

$ npm install mini-css-extract-plugin --save-dev

接下來我會介紹兩種設定

第一種,各別輸出在 js import css 檔案

編輯 webpack.config.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: {
    index: './index.js',
    app: './app.js',
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js',
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: './css/[name].bundle.css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(js)$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};

此範例是多檔輸入輸出,可以回顧看 webpack 基礎設定

  1. 引入 MiniCssExtractPlugin 模組
  2. 加入 plugins 設定 ,輸出 CSS 檔案
  3. 將原本 style-loader 改成 MiniCssExtractPlugin.loader

註:filename 使用[name]變數,它會自動抓取你在 entry 裡的屬性名稱來命名檔案

執行編譯

這邊我使用兩個 js 跟兩個 css 來作範例

JS 檔案

index.js 引入了一支 CSS

app.js 引入了兩支 CSS

CSS 檔案

分別寫不同的內容

$ npm run start

編譯出來的結果

可以看到 dist 裡面多了 CSS 資料夾,並且編譯出兩支 CSS 檔案(根據 JS 裡面引入的 CSS 打成一包)

第二種,將所有在 js import css 檔案,輸出一支 CSS

編輯 webpack.config.js

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

module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: {
    index: './index.js',
    app: './app.js',
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js',
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: './css/[name].bundle.css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(js)$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};

加入 optimization.splitChunks

強制 CSS 打成一包

註:optimization.splitChunks 是在 webpack 4 新出針對模組做優化的設定( 替代了 CommonsChunkPlugin)

編譯出來的結果

會發現多了兩個檔案,CSS 是我們要的,至於那個多出來的 JS 是沒有用處的,目前查詢的結果應該算是 webpack 4 splitChunks 的小 Bug,看 Issues 上討論的結果,目前應該是沒有好的解決方法,只能手動刪除檔案 ( 算是最笨的方法 ( 笑,或忽略它不用管。

註:此問題應該會在 webpack 5 修復

Issues:

Single-file configuration emits JS assets in its own chunk group #85

Don’t output empty JS files #151

接下來介紹如何 CSS minify ,在 webpack 4 筆記(1) 有提到 webpack 有一個執行參數 --mode production,將檔案給壓縮跟優化。但他只能壓縮 JS,卻不能壓縮 CSS 檔案。

這時候要另外使用 optimize-css-assets-webpack-plugin

optimize-css-assets-webpack-plugin

用於優化\最小化 CSS 內容

安裝

$ npm install optimize-css-assets-webpack-plugin --save-dev

設定

編輯 webpack.config.js

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

module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: {
    index: './index.js',
    app: './app.js',
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js',
  },
  optimization: {
    minimizer: [new OptimizeCSSAssetsPlugin()],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: './css/[name].bundle.css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(js)$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};
  1. 引入 optimize-css-assets-webpack-plugin 模組
  2. 設定 optimization.minimizer

執行編譯

注意這邊要執行 deploy ( npm 指令可閱 Webpack 基礎設定 )

$ npm run deploy

CSS 成功被壓縮了,可是 JS 壓縮卻失效,為什麼?

因為 optimization.minimizer 會將預設值覆蓋掉,minimizer 沒有 JS 壓縮的設定,導致 JS 壓縮並沒有執行,所以我們還要加上另一個套件。

terser-webpack-plugin

用於壓縮 JavaScript

安裝

$ npm install terser-webpack-plugin --save-dev

設定

編輯 webpack.config.js

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

module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: {
    index: './index.js',
    app: './app.js',
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js',
  },
  optimization: {
    minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: './css/[name].bundle.css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(js)$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};
  1. 引入terser-webpack-plugin模組
  2. 設定 optimization.minimizer

執行編譯

$ npm run deploy

CSS JS 都有順利壓縮,編譯成功!!

題外話

在使用 mini-css-extract-plugin 之前,很多網站推薦使用 extract-text-webpack-plugin,使用後卻會跳出 npm ERR!

經查詢後發現,原來

webpack 4 已棄用 extract-text-webpack-plugin

結語

寫到現在,真的覺得 webpack 比 gulp 還難學,查詢了很多文章,經過多項測試,才刪減出給大家最簡潔的使用方法,其實還有非常多小細節沒有介紹到,很多我自己也都還沒有理解 XD, 如果有說明錯誤或不明白的地方,麻煩再留言告知我,謝謝!


參考資料

mini-css-extract-plugin

optimize-css-assets-webpack-plugin

terser-webpack-plugin

Webpack 實作入門 2:打包 CSS / SCSS 與 加入 Bootstrap