from: https://segmentfault.com/a/1190000014722978
CSS的痛点
1、CSS 的规则是全局的,任何一个组件的样式规则,都对整个页面有效。相信写css的人都会遇到样式冲突(污染)的问题。
2、为了解决全局污染的问题,那就把class命名写长一点吧、加一层父级选择器、降低冲突的几率,那么CSS命名混乱了
3、组件依赖管理不彻底,组件应该相互独立,引入一个组件时,应该只引入它所需要的 CSS 样式。
4、代码难以复用,出现了sass less 之类的工具
使用 CSS Modules
CSS Modules不是将CSS改造的具有编程能力,而是加入了局部作用域、依赖管理,这恰恰解决了最大的痛点。可以有效避免全局污染和样式冲突,能最大化地结合现有 CSS 生态和 JS 模块化能力。
启用 CSS Modules
webpack 自带的 css-loader
组件,自带了 CSS Modules,通过简单的配置即可使用。
module.exports = { entry: path.join(__dirname, ‘/src/index.js‘), output: { filename: ‘bundle.js‘, path: path.resolve(__dirname, ‘dist‘) }, module: { rules: [ { test: /\.css$/, use: [ ‘style-loader‘, { loader: ‘css-loader‘, options: { modules: true, } } ] } ] } }
在上述配置的基础上
/* Button.css */ .primary { background-color: #1aad19; color: #fff; border: none; border-radius: 5px; }
// Button.js import styles from ‘./Button.css‘; console.log(styles); // -> {primary: "yTXmm0isaXExoYiZUvKxH"} const Button = document.createElement(‘div‘) Button.innerHTML = `<button class=${styles.primary}>Submit</button>` export default Button
// index.js import Button from ‘./components/Button‘ const app = document.getElementById(‘root‘) app.appendChild(Button)
生成的html文件:
<div id="root"> <div> <button class="yTXmm0isaXExoYiZUvKxH">Submit</button> </div> </div> <!-- yTXmm0isaXExoYiZUvKxH为CSS Modules自动生成的class类名 -->
CSS Modules自动生成的class类名基本就是唯一的,大大降低了项目中样式覆盖冲突的几率。
定制class类名
css-loader
默认的哈希算法是`[hash:base64]`
,从前面我们可以发现.primary
被编译成了 yTXmm0isaXExoYiZUvKxH 这样的字符串。这名字也没风格了别担心,css-loader 为我们提供了localIdentName
参数指定生成的名字格式,其默认值是[hash:base64]
。
{ loader: ‘css-loader‘, options: { modules: true, localIdentName: ‘[name]__[local]--[hash:base64:5]‘ } }
局部作用域
通过前面CSS module的例子我们发现它思路很简单就是生成唯一的class类名。CSS module将class转换成对应的全局唯一hash值来形成局部作用域。使用了 CSS Modules 后,就相当于给每个 class 名外加了一个 `:local(.className)
`。这是默认的,也可以显式使用。
如果想切换到全局模式,CSS Modules 允许使用:global(.className)
的语法,声明一个全局规则。凡是这样声明的class
,都不会被编译成哈希字符串。
/* Button.css */ :global(.btn) { color: #fff; border: none; border-radius: 5px; } .primary { background-color: #1aad19; } /* 显式的局部作用域语法 与上面不加`:local`等价 */ :local(.warn) { background-color: #e64340 }
CSS Modules下的样式复用
对于样式复用,CSS Modules 提供了 composes
组合 的方式来处理。一个选择器可以继承另一个选择器的规则。
/* Button.css */ .btn { /* 所有通用的样式 */ color: #fff; border: none; border-radius: 5px; box-sizing: border-box; } .primary { composes: btn; /* 继承btn的样式 */ background-color: #1aad19; }
生成的 HTML 变为
<div id="root"> <div> <button class="Button__primary--yTXmm Button__btn--nx67B">Submit</button> </div> </div>
composes 还可以也可以继承组合其他CSS文件里面的规则
/* author.css */ .shadow { box-shadow: 0 0 20px rgba(0, 0, 0, .2) }
/* Button.css */ ... .primary { composes: btn; composes: shadow from ‘./author.css‘; background-color: #1aad19; }
总结
CSS Modules 很好的解决了 CSS 目前面临的一些痛点以及模块化难题,同时也支持与 Sass/Less/PostCSS 等搭配使用。
无论是通过遵循的命名标准化的规范(BEM、OOCSS、AMCSS、SMACSS),还是使用本文介绍的CSS Modules,目的都是一样:可维护的css代码。具体使用不是有还是要结合自己的场景来决定。
补充:BME
B
意思是Block 块
,E
意思是Element 元素
,M
意思是Modifier 修饰器
。块与元素之间使用 __ 双下划线
连接,块或元素与修饰器之间使用 -- 双中划线
连接。
BEM
中块、元素、修饰器的命名如果存在多个单词使用 - 单中划线
连接。
<button class="button"> Normal Button </button> <button class="button--state-success"> Success Button </button> <div class="footer"> <button class="footer__button">footer button</button> <button class="footer__button--state-success">footer button</button> </div>
.button {} /*基础按钮*/ /*通过双中划线连接修饰器 这样语义化更加鲜明*/ .button--state-success {} /*状态为成功的button*/ .footer__button{} /*底部按钮样式*/ .footer__button--state-success{} /*底部状态为成功的按钮样式*/ /*state-success 多个单词直接使用-连接*/