CSS Modules加入了局部作用域、依赖管理。可以有效避免全局污染和样式冲突,能最大化地结合现有CSS生态和JS模块化能力。
webpack中的css-loader自带了CSS Modules,通过简单的配置即可使用。
// webpack.config.js
const path = require('path')
module.exports = {
entry: __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,
}
}
]
}
]
}
}
// 也可以使用下面这种写法
// loader: "style-loader!css-loader?modules"
// exclude排除及include包含,使用公共样式
// module: {
// loaders: [{
// test: /\.jsx?$/,
// loader: 'babel',
// }, {
// test: /\.scss$/,
// exclude: path.resolve(__dirname, 'src/styles'), //排除styles文件夹内的scss文件,因为这是common公共样式
// loader: 'style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true',
// }, {
// test: /\.scss$/,
// include: path.resolve(__dirname, 'src/styles'), //但给styles文件夹内的scss文件,使用style!css!sass loader
// loader: 'style!css!sass?sourceMap=true',
// }]
// }
生成HTML为
<div id="root">
<div>
<button class="yTXmm0isaXExoYiZUvKxH">Submit</button>
</div>
</div>
<!-- yTXmm0isaXExoYiZUvKxH为CSS Modules自动生成的class类名 -->
CSS Modules 对 CSS 中的 class 名都做了处理,使用对象来保存原 class 和混淆后 class 的对应关系。CSS Modules自动生成的class类名基本就是唯一的,大大降低了项目中样式覆盖冲突的几率。
定制class类名
css-loader默认的哈希算法是[hash:base64],从前面我们可以发现.primary 被编译成了 yTXmm0isaXExoYiZUvKxH 这样的字符串。这名字也没风格了别担心,css-loader 为我们提供了localIdentName参数指定生成的名字格式。localIdentName的默认值是[hash:base64]。
...
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]--[hash:base64:5]'
}
}
// 或者
loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]--[hash:base64:5]'
...
样式默认局部及开启CSS Modules全局样式
使用了CSS Modules后,样式默认给每个class名外加加了一个 :local,以此来实现样式的局部化,如果你想切换到全局模式,使用对应的 :global。
.normal {
color: green;
}
/* 以上与下面等价 */
:local(.normal) {
color: green;
}
/* 定义全局样式 */
:global(.btn) {
color: red;
}
/* 定义多个全局样式 */
:global {
.link {
color: green;
}
.box {
color: yellow;
}
}
CSS Modules结合React实践
/* dialog.css */
.root {}
.confirm {}
.disabledConfirm {}
import classNames from 'classnames';
import styles from './dialog.css';
export default class Dialog extends React.Component {
render() {
const cx = classNames({
[styles.confirm]: !this.state.disabled,
[styles.disabledConfirm]: this.state.disabled
});
return <div className={styles.root}>
<a className={cx}>Confirm</a>
...
</div>
}
}
注意,一般把组件最外层节点对应的 class 名称为 root。这里使用了classnames库来操作 class 名。
如果你不想频繁的输入styles.**,可以试一下react-css-modules,它通过高阶函数的形式来避免重复输入styles.**。