文章同步於Github Pines-Cheng/blogjavascript
隨着前端這幾年的風生水起,CSS做爲前端的三劍客之一,各類技術方案也是層出不窮。從CSS prepocessor(SASS、LESS、Stylus)到後來的後起之秀 PostCSS
,再到 CSS Modules
、Styled-Component
等。有人維護了一份完整的 CSS in JS 技術方案的對比,裏面已經有將近50種技術方案。CSS Modules
就是其中一種。css
要弄懂CSS Modules
是什麼,能夠先看官方介紹:GitHub – css-modules/css-modules: Documentation about css-modules。html
經過上面介紹能夠看出,CSS Modules
既不是官方標準,也不是瀏覽器的特性,而是在構建步驟(例如使用Webpack或Browserify)中對CSS類名選擇器限定做用域的一種方式(經過hash實現相似於命名空間的方法)。例如咱們在buttons.js裏引入buttons.css文件,並使用.btn的樣式,在其餘組件裏是不會被.btn影響的,除非它也引入了buttons.css.前端
JS已經全面實現了模塊化,可是css還處於探索階段。爲何咱們須要css模塊化?主要由一下幾個緣由。java
CSS的規則都是全局的,任何一個組件的樣式規則,都對整個頁面有效。如今的前端工程大可能是基於組件開發,隨着工程的頁面數量好複雜度的提高,相信寫css的人都會遇到樣式衝突(污染)的問題。通常咱們會採用一下幾種方法:react
class命名寫長一點吧,下降衝突的概率webpack
加個父元素的選擇器,限制範圍git
從新命名個class吧,比較保險github
但是以上方案只是下降了全局衝突的機率,並不能完全解決全局衝突的問題。而且,實現方式也不夠優雅,還增長了代碼的複雜和冗餘。web
面向組件開發 : 處理 UI 複雜性的最佳實踐就是將 UI 分割成一個個的小組件,React 就鼓勵高度組件化和分割。咱們但願有一個 CSS 架構去匹配。
沙箱化(Sandboxed
) : 若是一個組件的樣式會對其餘組件產生沒必要要以及意想不到的影響,那麼將 UI 分割成組件並無什麼用。就這方面而言,CSS的全局做用域會給你形成負擔。
方便 :不會增長開發的負擔和代碼的冗餘。
CSS 模塊化的解決方案有不少,但主要有三類。
規範化CSS的模塊化解決方案(好比BEM BEM — Block Element Modifier ,OOCSS,AMCSS,SMACSS,SUITCSS)
但存在如下問題:
JS CSS之間依然沒有打通變量和選擇器等
複雜的命名
完全拋棄 CSS,用 JavaScript 寫 CSS 規則,並內聯樣式。styled-components 就是其中表明。styled-components可讓CSS真正意義地寫到JS裏面,同時讓標籤更具備語意化,這跟HTML5新標籤思想相同;該框架讓樣式也具有組件化思想,讓前端徹底面向組件化編程,就像java的包裝類型。
但存在如下問題:
樣式代碼也會出現大量重複。
不能利用成熟的 CSS 預處理器(或後處理器)
使用JS編譯原生的CSS文件,使其具有模塊化的能力,表明是 CSS Modules。
CSS Module仍是JS和CSS分離的寫法,不會改變你們的書寫習慣,CSS Module只需修改構建代碼和使用模塊依賴引入className的方式便可使用,且支持less和sass的語法,
使用CSS Modules可讓組件className控制權轉交給JS,咱們不會去關心命名衝突污染等問題,同時能夠靈活控制生成的命名,樣式代碼不用修改便可讓使用CSS語法的舊項目零成本接入。
CSS Modules 能最大化地結合現有 CSS 生態(預處理器/後處理器等)和 JS 模塊化能力,幾乎零學習成本。只要你使用 Webpack,能夠在任何項目中使用。是目前最好的 CSS 模塊化解決方案。
CSS Modules配置很是簡單,若是你使用webpack,只須要在配置文件中改動一行便可。
// webpack.config.js css?modules&localIdentName=[name]__[local]-[hash:base64:5]
加上 modules 即爲啓用,localIdentName 是設置生成樣式的命名規則。
css
/* components/Button.css */ .normal { /* normal 相關的全部樣式 */ }
js
// components/Button.js import styles from './Button.css'; console.log(styles); buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`
上例中 console 打印styles的結果是:
Object { normal: 'button--normal-abc53', disabled: 'button--disabled-def886', }
注意到 button--normal-abc53 是 CSS Modules 按照 localIdentName 自動生成的 class 名。其中的 abc53 是按照給定算法生成的序列碼。通過這樣混淆處理後,class 名基本就是惟一的,大大下降了項目中樣式覆蓋的概率。同時在生產環境下修改規則,生成更短的 class 名,能夠提升 CSS 的壓縮率。
CSS Modules 對 CSS 中的 class 名都作了處理,使用對象來保存原 class 和混淆後 class 的對應關係。
import React from 'react'; import styles from './table.css'; export default class Table extends React.Component { render () { return <div className={styles.table}> <div className={styles.row}> </div> </div>; } }
渲染結果:
<div class="table__table___32osj"> <div class="table__row___2w27N"> </div> </div>
babel-plugin-react-css-modules 能夠實現使用styleName屬性自動加載CSS模塊。只須要把className換成styleName便可得到CSS局部做用域的能力,babel插件來自動進行語法樹解析並最終生成className。改動成本極小,不會增長JSX的複雜度,也不會給項目帶來額外的負擔。
import React from 'react'; import styles from './table.css'; class Table extends React.Component { render () { return <div styleName='table'> </div>; } } export default Table;
CSS Modules 很好的解決了 CSS 目前面臨的模塊化難題。支持與 CSS處理器搭配使用,能充分利用現有技術積累。若是你的產品中正好遇到相似問題,很是值得一試。