CSS Modules实践

文章同步于Github Pines-Cheng/blogjavascript

随着前端这几年的风生水起,CSS做为前端的三剑客之一,各类技术方案也是层出不穷。从CSS prepocessor(SASS、LESS、Stylus)到后来的后起之秀 PostCSS,再到 CSS ModulesStyled-Component 等。有人维护了一份完整的 CSS in JS 技术方案的对比,里面已经有将近50种技术方案。CSS Modules就是其中一种。css

CSS Modules 介绍

要弄懂CSS Modules是什么,能够先看官方介绍:GitHub – css-modules/css-modules: Documentation about css-moduleshtml

经过上面介绍能够看出,CSS Modules既不是官方标准,也不是浏览器的特性,而是在构建步骤(例如使用Webpack或Browserify)中对CSS类名选择器限定做用域的一种方式(经过hash实现相似于命名空间的方法)。例如咱们在buttons.js里引入buttons.css文件,并使用.btn的样式,在其余组件里是不会被.btn影响的,除非它也引入了buttons.css.前端

CSS模块化

JS已经全面实现了模块化,可是css还处于探索阶段。为何咱们须要css模块化?主要由一下几个缘由。java

CSS全局做用域问题

CSS的规则都是全局的,任何一个组件的样式规则,都对整个页面有效。如今的前端工程大可能是基于组件开发,随着工程的页面数量好复杂度的提高,相信写css的人都会遇到样式冲突(污染)的问题。通常咱们会采用一下几种方法:react

  • class命名写长一点吧,下降冲突的概率webpack

  • 加个父元素的选择器,限制范围git

  • 从新命名个class吧,比较保险github

但是以上方案只是下降了全局冲突的几率,并不能完全解决全局冲突的问题。而且,实现方式也不够优雅,还增长了代码的复杂和冗余。web

咱们的追求

  • 面向组件开发 : 处理 UI 复杂性的最佳实践就是将 UI 分割成一个个的小组件,React 就鼓励高度组件化和分割。咱们但愿有一个 CSS 架构去匹配。

  • 沙箱化(Sandboxed) : 若是一个组件的样式会对其余组件产生没必要要以及意想不到的影响,那么将 UI 分割成组件并无什么用。就这方面而言,CSS的全局做用域会给你形成负担。

  • 方便 :不会增长开发的负担和代码的冗余。

方案

CSS 模块化的解决方案有不少,但主要有三类。

CSS 命名约定

规范化CSS的模块化解决方案(好比BEM BEM — Block Element Modifier ,OOCSS,AMCSS,SMACSS,SUITCSS)
但存在如下问题:

  • JS CSS之间依然没有打通变量和选择器等

  • 复杂的命名

CSS in JS

完全抛弃 CSS,用 JavaScript 写 CSS 规则,并内联样式。styled-components 就是其中表明。styled-components可让CSS真正意义地写到JS里面,同时让标签更具备语意化,这跟HTML5新标签思想相同;该框架让样式也具有组件化思想,让前端彻底面向组件化编程,就像java的包装类型。
但存在如下问题:

  • 样式代码也会出现大量重复。

  • 不能利用成熟的 CSS 预处理器(或后处理器)

使用 JS 来管理样式模块

使用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 的对应关系。

React实践

手动引用

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

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处理器搭配使用,能充分利用现有技术积累。若是你的产品中正好遇到相似问题,很是值得一试。

参考

相关文章
相关标签/搜索