A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. All URLs (url(...)) and @imports are in module request format (./xxx and ../xxx means relative, xxx and xxx/yyy means in modules folder, i. e. in node_modules).
因为默认 CSS Modules 会对样式表中的 class 名称做 :local() 处理。如果不想生成针对局部的 local name 怎么办? 想要使 class 名称不被 localIndentName 处理,可以使用 :global() 来指定 class 名称为全局的,在 :global 中的 class 在编译后不会改变。
接触 CSS Modules 的概念也有一段时间了,公司里和个人的一些项目中进行了不少的应用。这里对 CSS Modules 做一个简单的总结,结合自己使用的经历和心得说一说遇到过的问题和目前的理解。
CSS 工程化难题:
一、 CSS 全局污染
CSS 选择器作用于全局,虽然我们尽可能使用更规范的选择器使用规范,使用更“特殊”的选择器,但是在后期维护的过程中,仍然不可避免地发生 CSS 选择器 “污染”。
二、命名和选择器:
命名一直是比较让人头疼的问题,即使有很多现成的解决方案,但是很明显遵循一个规范写超长的变量名真的很麻烦……
选择器的使用也是一门艺术,在结构和交互复杂的页面中规划选择器样式是一个需要工程化解决的问题。
三、重用和管理:
CSS 的重用和管理在传统 CSS 中比较困难,也是工程化至关重要的事情,尤其是进行组件化开发的时候,组件之间的 CSS 独立性难以管理。
CSS 模块化解决方案:
一、LESS、SCSS & PostCSS
CSS 作为前端编程中占据基础地位的一部分,在工程化方面需要借助一定的工具去组织。如果只是靠规范去指定作用域,组织嵌套,会变得难以维护,更谈不上代码结构的清晰。
有很多 CSS 工程化方面的强有力工具,比如: LESS / SCSS,它们不仅是 CSS 的语法糖,更是为 CSS 编程提供了很多编程语言的特性,类似于变量、嵌套、函数、循环和模块等。而 PostCSS 则是集成了一系列 CSS 工具链,让我们可以使用构建工具,例如常用的 autoprefixer 为我们添加支持多浏览器的前缀等。
这些工具的优点是:
缺点是什么呢?
这个优缺点总结并不充分,对 SCSS 和 LESS、autoprefixer 熟悉但是并没有用到全部特性,从一个使用者的角度来看,我非常喜欢 SCSS 和 LESS 等的方式。
二、CSS in JS / JSON
这种方案是直接在 JS 中编写样式,例如 material-ui 。可以使用 radium 这种类似的库。在 JS 中管理样式解决了模块化的问题,但是不能使用 LESS、SCSS、PostCSS预处理器,如 :focus、 :active、:hover 伪类处理起来比较麻烦。
三、CSS with JS
CSS Modules 把 CSS 生态 和 JS 的模块化能力结合起来,用简洁的 API 和 webpack 这样的工具做到了 CSS 的模块化。
CSS Modules 是什么
CSS Modules
我的大概理解是:
一个 CSS Module (CSS 模块) 就是一个文件,这个文件中定义的所有 class 名和 animation 名都是默认只是针对局部作用域的。所有 URL 和 @import 形式的引入如果以 './' 或者 '../' 等开头都会从相对路径去找;而 'xxx/yyy' 形式的引用则意味着会从类似 'node_modules' 的外部包中去找。
CSS Modules 会被编译成一种叫做 ICSS 的格式,但是这个是针对 loader 的,而非前端用户。写法上,基本和正常的 CSS 语法一致:
当在 JS 模块中引入 CSS 模块时,CSS 模块会 export 一个包含着所有 local name(局部变量) 到 global name(全局变量) 映射的对象,我们使用 local name(局部变量) 作为键值,可以从这个对象中取到相应的 global name(全局变量)。
CSS Modules 使用
一、 在项目中引入 CSS Modules:
使用 webpack 项目中,引入 css-loader,css-loader 支持 CSS Modules,需要在 webpack.config.js 中加入配置:
在 JS 模块中引入 CSS 文件:
相应的 CSS 为:
变量格式可以由类似: [name]__[local]-[hash:base64:8] 去指定,具体参见 css loader 文档。
二、 命名:
官方推荐的命名方式是使用驼峰法,大概是因为驼峰法可以直接用 . 操作符取到相应的键值;我习惯上用中划线连接,对我来说更清晰一些。
三、 例外:
因为默认 CSS Modules 会对样式表中的 class 名称做 :local() 处理。如果不想生成针对局部的 local name 怎么办? 想要使 class 名称不被 localIndentName 处理,可以使用 :global() 来指定 class 名称为全局的,在 :global 中的 class 在编译后不会改变。
例如:
四、 组合:
可以组合 class,需要使用 composes。
例如:
可以有多个 composes 的规则,但是所有 composes 的规则必须在其他规则之前声明。composes 扩展只针对 local class,并且要求 local class 选择器只是一个单独的 class name。
如上图所示,当一个 local class A composes 了另外一个 local class B 的时候,会在标签上应用这两个 class name -> A & B。
五、 依赖:
可以再一个 CSS Modules 中引入其他 CSS Modules。
例如:
六、 结合其他编译器来使用:
CSS Modules 可以结合 LESS/SCSS 来使用,具体使用基本就是引入相应的 loader 即可。但是结合其他编译器会有一些问题,比如嵌套 和 composes 的冲突等。
CSS Modules 的好处
使用技巧