一、What?
css modules官方文档:CSS files in which all class names and animation names are scoped locally by default.
大致意思就是所有的类名和动画名称默认都有各自的作用域的CSS文件。
CSS Modules 并不是 CSS 官方的标准,也不是浏览器的特性,而是通过构建工具(比如 webpack)的帮助,将 class的名字或者选择器的名字生成一个独一无二的命名,从而实现作用域的隔离(类似命名空间化)。
二、Why?
为什么要用CSS Modules?或者可以这么说吧,CSS Modules为我们解决了什么痛点
1、解决了命名冲突和全局样式污染问题(因为CSS Modules只关注于组件本身,只要保证组件本身命名不冲突,就不会有这样的问题)
2、解决css选择器嵌套过深问题(因为CSS Modules只关注于组件本身,组件本身基本都可以使用扁平的类名来写)
3、样式模块化(一个css文件就是一个独立的模块)
三、How?
CSS Modules 是通过构建工具进行编译打包的,所以不局限于React、Vue还是Angular。
Create React App创建的react项目中,默认是开启 CSS Module 的。但是对于样式文件的命名有一个约束,需要以.module.less/css/sass结尾。
也可以通过配置webpack启用 CSS Module,来支持 .css 文件的后缀。本文是以webpack为例,来指引如何使用css modules。
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use:[
'style-loader',
{
loader: "css-loader",
options: {
modules: true // 启用 CSS 模块
}
}
]
}
]
}
};
示例a:
// JSX
import React from 'react';
import style from './index.css';
const Content = () => {
return (
<div className={style.title}>Content title</div>
)
}
export default Content;
// css
.title {
color: #983636;
font-size: 40px;
}
编译后
定制哈希类名
css-loader默认class命名的哈希算法是[hash:base64],由上边的示例a可以看出class名可辨识度不高,我们可以开启自定义类名。
webpack.config.js里面可以定制哈希字符串的格式,css-loader提供了一个localIdentName参数,同时我们还可以利用这四个参数path、name、local和hash,四个参数的含义依次是:当前css、less或sass的文件路径,引入的模块名,局部作用域的类名,独一无二的hash值。
module.exports = {
module: {
rules: [
{
test: /.css$/,
use:[
'style-loader',
{
loader: "css-loader",
options: {
modules: {
localIdentName: '[path][name]__[local]--[hash:base64:5]'
}
}
}
]
}
]
}
};
编译后
和已有的普通css共存
在css-loader里通过指定test、include、exclude来区分它们。保持CSS Modules的纯净,只对想要应用CSS Modules的css文件,才启用CSS Modules。
module.exports = {
module: {
rules: [
// 启用 css module 模块
{
test: /\.css$/,
use:[
'style-loader',
{
loader: "css-loader",
options: {
modules: true
}
}
],
exclude: /styles/
},
// 不启用 css module 模块
{
test: /\.css$/,
use:[
'style-loader',
'css-loader'
],
include: /styles/
}
]
}
};
四、CSS Modules 特性:
1、作用域
CSS Modules分局部作用域和全局作用域。
两者的区分是通过:local() 与:global()来设定的。因为CSS Module默认的是局部作用域,所以 :local()默认省略。CSS Modules 使用:global(.className)的语法,声明一个全局规则。凡是这样声明的class,都不会被编译成哈希字符串,使用全局样式时直接赋值给class就行了,不需要进行类绑定。
局部
// Jsx
import React from 'react';
import style from './index.css';
const Index = () => {
return (
<div className={style.title}>Content title</div>
)
}
export default Index
// css
.title {
color: #983636;
font-size: 40px;
}
编译后
全局
// Jsx
import React from 'react';
import style from './index.css';
const Index = () => {
return (
<div className={style.header}>
<h4 className='title'>全局作用域</h4>
</div>
)
}
export default Index
// css
.header :global(.title) {
font-size: 36px;
text-align: center;
}
编译后
2、命名
对于局部类名称,CSS Modules 更推荐以驼峰式camelCase的命名方式定义类名,即 styles.className。也可以使用 styles['class-name'],允许但不提倡。
3、class类名组合
CSS Module可以将两个选择器的样式组合在一起。也就是说,一个选择器可以继承另一个选择器的样式,通过composes来实现。
- 组合当前样式表的class
import React from 'react';
import style from './index.css';
const Index = () => {
return (
<div className={style.currentComposes}>
类名组合-当前css文件
</div>
)
}
export default Index
.fontSize {
font-size: 40px;
}
.currentComposes {
composes: fontSize;
text-align: center;
}
编译后
- 组合其他样式表的class
import React from 'react';
import style from './index.css';
const Index = () => {
return (
<div className={style.otherComposes}>类名组合 其他css文件</div>
)
}
export default Index
// another.css
.textAlign {
text-align: center;
}
// 当前css可以继承 another.css里面类名。
.otherComposes {
font-size: 24px;
composes: text-align from '../../other.css';
}
编译后
4、变量
使用 @value 来定义变量
import React from 'react';
import style from './index.css';
const Index = () => {
return (
<div className={style.value}>
变量
</div>
)
}
export default Index
在colors.css里面定义变量
/*colors*/
@value v-primary: #36AA6B;
@value v-bg-color: #ecf7f0;
当前css可以引用这些变量。
@value colors: '../../styles/colors.css';
@value v-primary, v-bg-color from colors;
@value fontSize: 36px;
/*变量*/
.value {
color: v-primary;
background-color: v-bg-color;
font-size: fontSize;
}
编译后
当然也可以将 CSS Modules
与 Sass / Less
进行组合使用,从而既能拥有 Sass / Less
的 CSS 预处理器的能力(规则、变量、混入、选择器、继承等),又可以拥有 CSS Modules
提供的局部作用域的能力,避免全局污染。
本文demo地址:css-modules