CSS Modules使用詳解

  • 2019 年 12 月 4 日
  • 筆記

CSS Modules

CSS 模組化

不管是用jquery還是react開發,都會遇到的一系列 CSS 的問題:

  1. 全局污染
  2. 命名混亂
  3. 依賴引入複雜
  4. 無法共享變數
  5. 程式碼冗餘

通過 JS 來管理 CSS 就很好解決上述列舉的問題。CSS 模組化的解決方案有很多,但主要有兩類。

一類是徹底拋棄 CSS,使用 JS 或 JSON 來寫樣式。Radium, jsxstyle ,react-style 屬於這一類。優點是能給 CSS 提供 JS 同樣強大的模組化能力;缺點是不能利用成熟的 CSS 預處理器(或後處理器) Sass/Less/PostCSS, :hover 和 :active 偽類處理起來複雜。

另一類是依舊使用 CSS,但使用 JS 來管理樣式依賴,代表是CSS Modules。CSS Modules 能最大化地結合現有 CSS 生態和 JS 模組化能力。發布時依舊編譯出單獨的 JS 和 CSS。它並不依賴於 React,只要你使用 Webpack,可以在 Vue/Angular/jQuery 中使用。

啟用 CSS Modules

// webpack.config.js  css?modules&localIdentName=[name]__[local]-[hash:base64:5]

加上 modules 即為啟用, localIdentName 是設置生成樣式的命名規則,[name]表示標籤名,[local]表示類名,[hash:base64:5]是按照給定演算法生成的序列碼。

/* components/test.css */  .active {      color: red;  }  .disabled {      color: gray;  }    /* components/test.js */  import styles from './test.css';    console.log(styles);    elem.outerHTML = `<h1 class=${styles.active}>CSS Modules</h1>`

生成的 HTML 是

<h1 class="h1--active-abc53"> Processing... </h1>

它將根據 styleName 的值在關聯的 style 對象中查找對應的 CSS Modules,並為 ReactElement className 屬性值添加相匹配的獨一無二的 CSS 類名。

上例中 styles的consolelog 列印的結果是:

Object {    active: 'h1--active-abc53',    disabled: 'h1--disabled-def84',  }

CSS Modules 對 CSS 中的 class 名都做了處理,使用對象來保存原 class 和訂製處理後的 class 的對應關係。經過這樣類名訂製處理後,class 名基本就是唯一的,大大降低了項目中樣式覆蓋的幾率。同時可以生成更短的 class 名,減少程式碼量。

CSS Modules 使用

局部變數和全局變數

  • :local: 做 localIdentName 規則處理
  • :global: 樣式編譯後不變

如果書寫時不加,默認處理為:local

.normal {    color: green;  }    :local(.normal) {    color: green;  }    /* 上面兩個等價,默認給每個 class 名外加加了一個 `:local` */      /* 全局樣式 */  :global {    .link {      color: green;    }    .box {      color: yellow;    }  }

唯一哈希類名

開啟CSS Modules時定義的規則localIdentName=[name]__[local]-[hash:base64:5]會控制對class名的處理, [hash:base64:5]定義的hash計算能保證類名的唯一性。

Compose 組合Class

很多時候我們都需要樣式復用,在 CSS Modules 中,一個選擇器可以繼承另一個選擇器的規則,這稱為composes 組合。

/* components/test.css */  .bg {    background-color: blue;  }    .title {    composes: bg;    color: white;  }    /* components/test.js */  import styles from './test.css';    elem.outerHTML = `<h1 class=${styles.title}>CSS Modules</h1>`

生成的 HTML 為

<h1 class="h1--bg-fec53 h1--title-Ijf8"> Processing... </h1>

CSS和JS變數共享

:export 關鍵字可以把 CSS 中的 變數輸出到 JS 中:

/* index.scss */  $primary-color: #f40;    :export {    primaryColor: $primary-color;  }    /* app.js */  import style from 'index.scss';    // 會輸出 #F40  console.log(style.primaryColor);

CSS Modules使用特點

  1. 不使用選擇器,只使用 class 名來定義樣式
  2. 不層疊多個 class,只使用一個 class 把所有樣式定義好
  3. 不嵌套class
  4. 所有樣式通過 composes 組合來實現復用

舊項目的兼容

  1. 如果對一個元素使用多個 class,樣式照樣生效。
  2. 如何在一個 style 文件中使用同名 class ,編譯後仍是同名的。
  3. 如果在 style 文件中使用了 id 選擇器,偽類,標籤選擇器,所有這些選擇器將不被轉換,原封不動的出現在編譯後的 css 中。即 CSS Modules 只會轉換 class 名相關樣式。
  4. 當類名經過編譯生成新的隨機類名後,可以解決命名衝突,但因為無法預知最終 class 名,不能通過一般選擇器覆蓋。現在一般給出的項目中的實踐是可以給組件關鍵節點加上 data-role 屬性,然後通過屬性選擇器來覆蓋樣式。
  5. 前端項目不可避免會引入 normalize.css 或其它一類全局 css 文件。使用 Webpack 可以讓全局樣式和 CSS Modules 的局部樣式和諧共存。
module: {    loaders: [{      test: /.jsx?$/,      loader: 'babel'    }, {      test: /.scss$/,      exclude: path.resolve(__dirname, 'src/styles'),      loader: 'style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true'    }, {      test: /.scss$/,      include: path.resolve(__dirname, 'src/styles'),      loader: 'style!css!sass?sourceMap=true'    }]  }