CSS Modules

CSS命名規範,基本又是每一個團隊都要面臨的問題,規範一定會出一則,但最後能嚴格執行的,始終是一件很難的事。css

剛來到公司團隊的時候,你們都遵照使用『基於姓氏命名』法則併爲之推廣html

以下面常見的商品信息模塊結構:前端

<div class="gb_goods_item">
	<div class="gb_goods_item_box">
	  <div class="gb_goods_item_image">
	    ...
	  </div>
	  <div class="gb_goods_item_info">
	    <div class="gb_goods_item_title">
	      ...
	    </div>
	    <div class="gb_goods_item_sidebar">
	      <div class="gb_goods_item_price">
	        ...
	      </div>
	      <div class="gb_goods_item_shopcart">
	        <div class="gb_shopcart">
	          ...
	        </div>
	      </div>
	    </div>
	  </div>
	</div>
</div>
複製代碼

這套法則注重 CSS 語義的表達和管理,初衷就是爲了方便區分樣式模塊的歸屬,也方便往後複用,有點像 BEM 方法。但缺點也很明顯:難理解,命名須要花額個的時間考慮其獨特性,當『後代』結構代碼至關複雜的時候,命名會太過沉長,不夠靈活,再有就是始終沒法根治代碼污染和被污染的問題。webpack

剛開始使用的時候,至關不習慣,爭議也多,每次有新員工來都須要花很多時候解釋,亦很難讓之 100% 地樂意使用,即使有明確的團隊規範文檔,真正上線的時候,仍是會出現『不規範』的代碼。git

儘管如此,權衡了當時的利弊,仍是堅持用了下來。當 Node 的出現和不斷強大,前端界獲得了的極速的發展,前端工做流更爲細緻。React 的橫空出世,突顯超大型高效協做收益的組件化更是深得人心,而組件化的獨立性及高複用性註定要解除 CSS 對組件的限制:樣式代碼污染問題。github

『基於姓氏命名』顯然不能知足這個要求,畢竟,CSS 樣式老是全局做用的,『姓名』再獨特,總會有同名同姓的機會。此時,CSS Modules 提供了一個很好的解決方案。web

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default.數據結構

CSS 文件中的全部類名和動畫名的做用域都默認爲當前做用域。yii

CSS Modules 給了 CSS 域的概念,先看一波 Css Modues 在實際應用上是怎麼的一回事。ide

React 組件中常見的輪播圖:

JSX 的 Render 內容:

import styles from './styles/FocusSlider.css';

// FocusSlider
render() {
  return (
	  <div className={styles.fsSlider} data-role="fsSlider">
	    <div className={styles.fsSliderMain}>
	      <Slider
	        sliderData={this.state.sliderData}
	      />
	    </div>
	    <div className={styles.fsSliderBg}></div>
	    {/* 數據重加載提示 */}
	    <DataReqReload
	      isShow={this.state.isReloadData}
	      doReload={this.doReload}
	    />
	  </div>
  );
}
複製代碼

對應該的 FocusSlider.scss

:local{
  .fsSlider{
    position: relative;
    padding-top: -webkit-calc(100vw * 120 / 1125);
    padding-top: calc(100vw * 120 / 1125);
  }

  .fsSliderMain{
    position: relative;
    z-index: 1;
    min-height: -webkit-calc( ((100vw - 60px) * 570 / 960) + ((100vw - 60px) * 70 / 1125) );
    min-height: calc( ((100vw - 60px) * 570 / 960) + ((100vw - 60px) * 70 / 1125) );
  }
  
  .fsSliderBg{
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: -webkit-calc(100vw * 588 / 1125);
    height: calc(100vw * 588 / 1125);
    background: #000 url(../../../images/top100/fs_bg.jpg) no-repeat;
    background-size: 100% auto;
  }

  .gbSlider{
    background: #000;
  }
}
複製代碼

編譯後的代碼是這樣的:

<div class="_1zbnOjlYzV-MBq1JhiGcZF" data-role="fsSlider">
    <div class="svXZ19tF2m_fbg8KuFb6T">
        <div class="lvyQyG394l5agFQKH8_du" data-role="gbSlider" style="opacity: 1;">
            ...
        </div>
    </div>
    <div class="_27Jm0ogj1EtOf2dNHKvKC1"></div>
    <!-- 數據重加載提示 -->
    <div class="f1qHBJK89yiiDn4Tcb2It _3grBcj2DcL0IJGuHXl4LK6" data-role="reloadBox">
        ...
    </div>
</div>
複製代碼

能夠看到,FocusSlider.scss 裏面對應定義的局部域樣式名都變成了一個哈希字符串,同時FocusSlider.css文件也會被編譯

._1zbnOjlYzV-MBq1JhiGcZF {
    position:relative;
    padding-top:calc(100vw * 120 / 1125)
}
.svXZ19tF2m_fbg8KuFb6T {
    position:relative;
    z-index:1;
    min-height:calc(((100vw - 60px) * 570 / 960) + ((100vw - 60px) * 70 / 1125))
}
._27Jm0ogj1EtOf2dNHKvKC1 {
    position:absolute;
    left:0;
    right:0;
    top:0;
    height:calc(100vw * 588 / 1125);
    background:#000 url(/static/media/fs_bg.70affa5b.jpg) no-repeat;
    background-size:100% auto
}
._2EHIbU8ioFFyvcDBD64P4_ {
    background:#000
}
複製代碼

Css Modules 使得樣式的類名在局部做用域內獨一無二而不會對全局的樣式形成污染,這正是組件化所急需的,彷佛也是爲 React 而生。

這樣一來,上面『基於姓氏命名』商品信息結構用 Css Moduels 就能夠這樣寫了:

import styles from '../styles/GoodsItem.css';

render() {
  return (
    <div class={styles.goodsItem}> <div class={styles.itemBox}> <div class={styles.image}> ... </div> <div class={styles.info}> <div class={styles.title}> ... </div> <div class={styles.sideBar}> <div class={styles.price}> ... </div> <div class={styles.shopCart}> <ShopCart sku={} /> </div> </div> </div> </div> </div> ); } 複製代碼

這樣咱們就能夠更專一組件的開發了。那麼如何使用?其實用起來很簡單

  • 若是你是用 React,而且使用偉大的 Create React App,默認是支持 Css Modules 的。
  • 若是你是用 Vue,恭喜你,並不用爲這個而操心,Vue 自帶樣式域的處理;
  • 若是都不是,那麼你可能須要 Webpack 的 css-loader,裏面你還能夠定製哈希類名。
  • 你還能夠用 PostCSS-ModulesPostCSS 的一個插件,在任何地方使用 Css Mudous 😳

更具體的文檔能夠看一下 github 上的 css-modules

也能夠看一下阮老師的《CSS Modules 用法教程》

Css Modules 用起來雖然很爽,但實際應用中,有一點美中不足,就是組件在局部做用域下的定製問題,最多見的就是『換膚』需求了,以下圖

上圖兩個商品列表,數據結構和數據來源都是同樣的,只是佈局和樣式略有不一樣,若是用傳統的 CSS 方案寫樣式,會先寫一個基準樣式,再寫『皮膚』樣式:

<div class="goods goods_a">
  <div class="goods_item">
    ...
  </div>
</div>

<div class="goods goods_b">
  <div class="goods_item">
    ...
  </div>
</div>
複製代碼

若是在組件系統中,上面的商品列表中,有多是這樣的結構:

在 Goods 的容器 GoodsContaniner 組件中

import styles from '../styles/GoodsContainer.css';

render() {
  return (
    <div className={style.goodsContainer}> <GoodsA> <GoodsItem /> </GoodsA> <GoodsB> <GoodsItem /> </GoodsB> </div>
  );
}
複製代碼

若是 GoodsAGoodsBGoodsItem 都是獨立的組件,Css Modules 對應的樣式也是局部做用域獨立的,GoodsAGoodsB 就不能對 GoodsItem 作樣式定製了。

對這種狀況,能夠在組件結構中爲樣式寫好擴展屬性,如在 GoodsItem 組件中寫成:

import styles from '../styles/GoodsItem.css';

render() {
  return (
    <div className={style.goodsItem} data-role="goodsItem"> ... </div>
  );
}
複製代碼

這樣在 GoodsAGoodsB 中就能夠控制到 GoodsItem

:local {
	.goodsA{
		[data-role="goodsItem"] {
			...
		}
	}
}
複製代碼

可能你會問,若是這樣寫那豈不是很麻煩?這種場景徹底能夠經過預規劃來規避,如規劃好 GoodsItem 組件的樣式做用域範圍(global 做用域)或提早設計好組件的結構,預留參數對 GoodsItem 組件進行樣式控制。

可是實際項目迭代過程當中,有時候咱們是沒法預知項目後續發展規模的,很大可能會在你初版寫好的組件中,不斷迭代功能,如上面提到的『換膚』場景,這就頗有必要寫好 data-role 爲組件的樣式作擴展了。

若是有項目使用組件系統的,CSS 方案的挑選,Css Modules 是不二的選擇。

某電影的一句臺詞也許能準確描述用過 CSS Modules 後的心情:『若是你 x 過驢仔 o,就 x 唔翻轉頭了』

相關文章
相關標籤/搜索