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 唔翻转头了』

相关文章
相关标签/搜索