捨棄 create-react-app 之餘還架了個 astro blog 昭告天下:webpack 5 與 css

今天會來介紹平常慣用的 webpack 設定,除了處理一般的 .css 檔案外,也能支援 css modules

使用 css modules 的好處是能夠將樣式限定在一個元件內。不需要煩惱樣式撞名、也比較容易處理 css specificity 的問題。不過,隨著 @layer 開始被各大瀏覽器支援,css modules 或許有退休機會 🤔 webpack 設定又能再少一段

有點離題了,先來看看如何在 webpack 中搞定 css 吧 =͟͟͞͞( •̀д•́)

設定 webpack

處理一般 .css 檔案

安裝完 style-loadercss-loader 後,在 webpack 的 module.rules 陣列加上以下內容:

const webpackDevelopmentConfig: WebpackConfiguration = {
  module: {
    rules: [
      {
        test: /\.css$/,
        exclude: /\.module\.css$/,
        sideEffects: true,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
};

透過 test: /\.css$/exclude: /\.module\.css$/ 來抓取「所有檔案名稱包含 .css 但不包含 .module.css 的檔案」,並使用 css-loaderstyle-loader 來處理之。

是的,只是要在開發環境讀取 css 檔案的話,這樣就夠了。

處理 css modules

追加如下,基本上僅是補上 css-loader 的 modules 功能設定:

const webpackDevelopmentConfig: WebpackConfiguration = {
  module: {
    rules: [
      {
        test: /\.css$/,
        exclude: /\.module\.css$/,
        sideEffects: true,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.module\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                mode: 'local',
                localIdentName: '[file]__[local]__[hash:6]',
              },
            },
          },
        ],
      },
    ],
  },
};

而因為個人習慣在開發時能夠明確看出 css class 名稱,所以會透過 localIdentName 來指定 module css 的組合方式。以上述設定來說,最終我會在開發環境看到以下 class name:

<div class="src-component-Common-Greeting-index-module-css__container__eb90c2">
  hello world
</div>

src-component-Common-Greeting-index-module-css 即為該 module css 的檔案路徑。container 則是該 module css 檔案中的樣式名稱。最後的 eb90c2 則是 localIdentNamehash:6 的部分。

全部可用的 template string 可以參考 css-loader 文件中關於 localIdentName 的段落


恭喜,今天的 webpack 部分到此結束。

設定 src/env.d.ts

而為了避免 TypeScript 在你 import *.module.css 檔案時抱怨,請開啟 ./src/env.d.ts 並填入以下內容:

declare module '*.module.css' {
  const classes: { readonly [key: string]: string };
  export default classes;
}

翻譯成白話文是「從 .module.css 取出來的資料都會是 { readonly [key: string]: string } 類型的檔案」。

比如下方範例,moduleStyle 的型別就是 { readonly [key: string]: string },而 moduleStyle.container 的型別會是字串:

import React from 'react';
import moduleStyle from './index.module.css';

function Greeting() {
  return <div className={moduleStyle.container}>hello world</div>;
}

export default Greeting;

總結

沒錯,就是這麼簡單。只要兩個 loader 跟一點點設定,你的 React app 專案就能在開發環境支援 css 與 css modules (́◉◞౪◟◉‵)