CSSConf 2015 筆記(二) – CSS Modules

CSS Modules 是過去一兩年來 CSS 領域最夯的話題了,尤其是 Web App 成長的速度高過 Content Site,CSS 層疊、覆寫、繼承的特性沒有幫到 Web App 反而造成污染,衍生了維護和 performance 方面的議題。兩位 CSS Modules 的作者從頭說起,分析從 global CSS  到 local CSS 的演變。

Document vs. Web App

所有學習 CSS 的人都知道 CSS Zen Garden,這是 web document 的代表。global CSS 對 document 來講沒什麼不好,每個篇章能簡潔地共用一份樣式。但 document 和 web app 最大的差異在 scale,善於寫樣式的人未必善於處理 scale。web app 結構複雜得多,巢狀和 reuse 也更頻繁,global CSS 有很大機率造成 class name 衝突和意外的繼承。

The End of Global CSS and The Rise of Modular CSS

開發社群過去嘗試過許多手段來解決這個問題,如 SMACSS, OOCSS, SUIT, BEM,甚至 inline styles。Sass 試圖以 @import partials 解決,但它只處理到 CSS,而且最後產出的仍是 global CSS。既然目前 web app 大量仰賴 Angular directive 和 React,作者提出未來(就是現在!)是 “component" 的時代,component 不是新東西,舉個簡單的例子,一個 <select> 下拉選單也可以看成一個 component。

當一個 project 使用許多 component 時,每個 component 的成員包含 js + css + images,所有圖片和 CSS 應該都只讓使用它們 component 看到 (CSS and images should be private to the components)。BEM 命名法與 React 合作良好,BEM 的 B (Block) 相當於 React 的各個 component。螢幕快照 2016-02-12 上午2.04.36

講者認為目前實踐 CSS Modules 的最佳工具是 Webpack,它能建立一條龍的 css/images/js 工作流程。
所以 CSS Modules 不像 Sass, PostCSS 是個 processor 可以用 npm 安裝,它是一種方法論,實踐這個方法論要用多種工具,Webpack 是其中最重要的一種。

在 Webpack 工作流程下,CSS 要在每個 component 裡這樣寫:

/* .myComponent.css */

:local(.className) {
 background: red;
 color: yellow;
}

經 Webpack hash 處理後變成類似這樣:

.myComponent__className_23_aKvs-b8bW2Vg3fwHozO {
  background: red;
  color: yellow;
}

也就是 Webpack 做了 BEM 的事情,但 BEM 是人工手動做, Webpack 是工具自動化地做,用 load javascript module 類似的方法處理 CSS,翻譯成機器看懂的語言。

Webpack 用到的主要套件是 css-loader,加上參數 module 來啟動 CSS Module 的處理:

/* webpack.config.js */

var css = require('css!./myComponent.css');
....
module.exports = {
  module: {
    loaders: [
      ...
      { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]') }
      ...
    ]
  },
....
}

因此任何 local CSS 都會得到獨一無二的 class name,繼承和層疊特性在 CSS Modules 完全被消滅,你不該期待與其他幾個 class 組合來補足缺少的樣式,每個 component 自己該有的樣式要全部寫好。
(以上只是觀念解說,我 Webpack 並不厲害,請勿當說明書看,看原版文件才是正辦)

https://github.com/css-modules/css-modules
https://github.com/webpack/css-loader

這裏有 demo:
https://github.com/css-modules/webpack-demo

有時候終究某些樣式需要維持 global,或需要用到第三方套件,關鍵字 :global 語法如下:

:global (.bodyClass) {
  background-color: gray;
}

這樣就不會被 Webpack 給予 hash name,而會被 compile 成 .bodyClass {}。
可以用 composes 組合多個來源的 CSS class:

:local(.className) {
  composes: edit hightlight from "./edit.css";
  composes: button from "module/button.css";
  composes: classFromThisModule;
  background: red;
}

在 React 這樣去取用樣式:

/* component/myComponent.jsx */

import styles from "./style.css";
// import { className } from "./style.css";

element.innerHTML = '<div class="' + styles.className + '">';

因為 CSS Modules 是蠻新的觀念,聽眾都很關心後續發展,問的問題都很厲害,記錄一下:
Q:要如何 debug?
A:自己拆分的 component,你應該會知道他來自哪個 module,撞名、不小心繼承問題也不存在,bug 只來自 global css 或是某個特定 module,並沒有交互污染的問題。至於如何拆分 component? It’s art.

Q:從 npm 安裝,到我的網站可以有個會動的 jquery date picker 這樣的東西需要多久?
A:jquery 套建中 CSS 某些部分可以當作 global 處理。

Q:可以和 bootstrap 這類的 framework 一起用嗎?
A:bootstrap 的思維是 global CSS,我們期待會有新一代的、符合 modular 思維的 framework 被發展出來。

看起來尚未有任何 framework 可以快速上手,只有自製的 CSS 有可能以 CSS Modules 的方法來管理。

《原始影片》


廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s