CSS Modules是什么东西呢?首先,让咱们从官方文档入手:
GitHub – css-modules/css-modules: Documentation about css-modulescss
CSS模块就是全部的类名都只有局部做用域的CSS文件。html
因此CSS Modules既不是官方标准,也不是浏览器的特性,而是在构建步骤(例如使用Webpack或Browserify)中对CSS类名选择器限定做用域的一种方式(经过hash实现相似于命名空间的方法)。react
在使用CSS模块时,类名是动态生成的,惟一的,并准确对应到源文件中的各个类的样式。webpack
这也是实现样式做用域的原理。它们被限定在特定的模板里。例如咱们在buttons.js里引入buttons.css文件,并使用.btn的样式,在其余组件里是不会被.btn影响的,除非它也引入了buttons.css.git
可咱们是出于什么目的把CSS和HTML文件搞得这么零碎呢?咱们为何要使用CSS模块呢?github
class命名写长一点吧,下降冲突的概率web
加个父元素的选择器,限制范围算法
从新命名个class吧,比较保险express
但是以上方案只是下降了全局冲突的几率,并不能完全解决全局冲突的问题。而且,实现方式也不够优雅,还增长了代码的复杂和冗余。npm
JS CSS没法共享变量
面向组件 – 处理 UI 复杂性的最佳实践就是将 UI 分割成一个个的小组件 Locality_of_reference 。若是你正在使用一个合理的框架,JavaScript 方面就将原生支持(组件化)。举个例子,React 就鼓励高度组件化和分割。咱们但愿有一个 CSS 架构去匹配。
沙箱化(Sandboxed) – 若是一个组件的样式会对其余组件产生没必要要以及意想不到的影响,那么将 UI 分割成组件并无什么用。就这方面而言,CSS的全局做用域会给你形成负担。
方便 – 咱们想要全部好的东西,而且不想产生更多的工做。也就是说,咱们不想由于采用这个架构而让咱们的开发者体验变得更糟。可能的话,咱们想开发者体验变得更好。
CSS 模块化的解决方案有不少,但主要有三类。
规范化CSS的模块化解决方案(好比BEM BEM — Block Element Modifier,OOCSS,AMCSS,SMACSS,SUITCSS)
但存在如下问题:
* JS CSS之间依然没有打通变量和选择器等
* 复杂的命名
完全抛弃 CSS,用 JavaScript 写 CSS 规则,并内联样式。 React: CSS in JS // Speaker Deck。Radium,react-style 属于这一类。但存在如下问题:
* 没法使用伪类,媒体查询等
* 样式代码也会出现大量重复。
* 不能利用成熟的 CSS 预处理器(或后处理器)
关注点分离:是将计算机程序分红不一样部分的设计原则,所以每一个部分都须要解决一个独立的问题。HTML主要用于组织网页内容,CSS用于定义内容呈现风格,JS定义内容如何与用户交互和行为。
维基百科:https://en.wikipedia.org/wiki/Separation_of_concerns#HTML.2C_CSS.2C_JavaScript
使用JS编译原生的CSS文件,使其具有模块化的能力,表明是 CSS Modules GitHub – css-modules/css-modules: Documentation about css-modules 。
CSS Modules 能最大化地结合现有 CSS 生态(预处理器/后处理器等)和 JS 模块化能力,几乎零学习成本。只要你使用 Webpack,能够在任何项目中使用。是笔者认为目前最好的 CSS 模块化解决方案。
webpack配置(版本为webpack4)
在 className 处直接使用 css 中 class 名便可。
注意到 table--table-32osj 是 CSS Modules 按照 localIdentName 自动生成的 class 名。其中的 32osj 是按照给定算法生成的序列码。通过这样混淆处理后,class 名基本就是惟一的,大大下降了项目中样式覆盖的概率。同时在生产环境下修改规则,生成更短的 class 名,能够提升 CSS 的压缩率。
CSS Modules 实现了如下几点:
* 全部样式都是局部做用域的,解决了全局污染问题
* class 名生成规则配置灵活,能够此来压缩 class 名
* 只需引用组件的 JS 就能搞定组件全部的 JS 和 CSS
* 依然是 CSS,几乎 0 学习成本
babel-plugin-react-css-modules 能够实现使用styleName属性自动加载CSS模块。咱们经过该babel插件来进行语法树解析并最终生成className。
以这种方式使用CSS模块有几个缺点:
您必须使用camelCase CSS类名称。
每当构造一个className时,你必须使用styles对象。
混合CSS模块和全局CSS类很麻烦。
对未定义的CSS模块的引用解决为未定义而没有警告。
您没必要强迫使用camelCase命名约定。
每次使用CSS模块时,不须要引用样式对象。
全局CSS和CSS模块之间有明显的区别,例如
那么该babel插件是怎么工做的呢?让咱们从官方文档入手:
翻译以下:
1. 构建每一个文件的全部样式表导入的索引(导入具备.css或.scss扩展名的文件)。
2. 使用postcss 解析匹配到的css文件
3. 遍历全部 JSX 元素声明
4. 把styleName 属性解析成匿名和命名的局部css模块引用
5. 查找与CSS模块引用相匹配的CSS类名称:
* 若是styleName的值是一个字符串字面值,生成一个字符串字面值。
* 若是是JSXExpressionContainer,在运行时使用helper函数来构建若是styleName的值是一个 jSXExpressionContainer, 使用辅助函数([getClassName]在运行时构造className值。
6. 从元素上移除styleName属性。
7. 将生成的className添加到现有的className值中(若是不存在则建立className属性)。
在成熟的项目中,通常都会用到CSS预处理器或者后处理器。
这里以使用了scss预处理器为例子,咱们来看下如何使用。
npm install -save-dev postcss-scss postcss-nested babel-plugin-react-css-modules
CSS Modules 很好的解决了 CSS 目前面临的模块化难题。支持与 CSS处理器搭配使用,能充分利用现有技术积累。若是你的产品中正好遇到相似问题,很是值得一试。