Skip to content

CSS Module

Css Module.pngCSS Modules 用法教程 - 阮一峰的网络日志

CSS Modules Github

【工程化】深入浅出 CSS Modules

什么是 CSS Module?

CSS Modules 既不是官方标准,也不是浏览器的特性,而是在构建步骤(例如使用 Webpack 或 Vite)中对 CSS 类名和选择器限定作用域的一种方式(类似于命名空间)。

目的:解决 CSS 中全局作用域的问题

CSS Modules 是一种 CSS 文件的模块化解决方案。 它的主要思想是将 CSS 文件作为一个模块,通过模块化的方式来管理 CSS 文件,避免全局污染,同时还能够实现 CSS 文件的复用。

开启 CSS Module

一般只需将 CSS 文件的后缀名改为 .module.css 即可使用 CSS Modules。

在 React 中默认开启了 CSS Module,样式表文件需要以 xxx.module.sass/less/css 命名

我们也可以通过配置 webpack 来开启 CSS Module

webpack.config.js

js
module.exports = {
	module: {
		rules: [
			{
				test: /\.css$/i,
				loader: "css-loader",
				options: {
					modules: true,
					localIdentName: "[name]_[local]__[hash:base64:5]"
				}
			}
		]
	}
};

localIdentName 可以定义生产的哈希类名,默认是 [hash:base64]

详细配置见: css-loader

localIdentName选项的占位符有:

text
 [name] 源文件名称 (样式文件的文件名)
 [folder] 文件夹相对于 compiler.context 或者 modules.localIdentContext 配置项的相对路径。
 [path] 源文件相对于 compiler.context 或者 modules.localIdentContext 配置项的相对路径。
 [file] - 文件名和路径。
 [ext] - 文件拓展名。
 [hash] - 字符串的哈希值。基于 localIdentHashSalt、localIdentHashFunction、localIdentHashDigest、localIdentHashDigestLength、localIdentContext、resourcePath 和 exportName 生成。
 [<hashFunction>:hash:<hashDigest>:<hashDigestLength>] - 带有哈希设置的哈希。
 [local] - 原始类名。

局部作用域

没有 CSS Module 的组件样式 默认 CSS 的规则是全局生效的,任何一个组件下的 CSS 样式,都会影响其他组件中使用相同类名的地方。

注意:

对于本地类名,我们推荐使用 camelCase。

虽然没有强制执行,但是 camelCase 是首选的,因为当尝试使用点符号访问 styles.class-name 时,kebab-case 可能会导致意外行为。 您仍然可以使用括号符号(例如 style [‘ class-name’])来处理 kebab-case,但是首选的是 styles.className。

CSS Module 是怎么局部作用 CSS 样式的?

答案:产生局部作用域的唯一方法,就是使用一个独一无二的 class 的名字,不会与其他选择器重名。这就是 CSS Modules 的做法。

这里就拿 React 项目来进行解释

在 React 中,默认是开启 CSS Module 的。但是对于样式表文件的命名一个约束。需要以.module.less/css/sass结尾

全局作用域(:global)

通常在我们的日常开发中有这种场景: 我们有一个自己的组件,但是这个组件使用了一些第三方的组件库,对于我们使用的第三方组件我们又想修改一下它的样式。

需要不对第三方组件的类名进行哈希,保留原始类名,才能起到样式覆盖的作用:global

:global(.className)那么此时这个 className 即使是在组件的样式表中定义的也不会被添加 hash 值, 所以就可以影响全局所有类名为 className 的样式

注意:

此时组件中对该类的样式修改会影响全局所有使用该类名的地方,所以为了将样式修改限制到本组件, 一般推荐将:global 使用在组件自定义类名范围下,然后添加这个自定义类名到组件中

class 的组合

在 CSS Modules 中,一个选择器可以继承另一个选择器的规则,这称为"组合"。

可以有多个合成规则,但合成规则必须在其他规则之前。 扩展仅对局部范围的选择器有效,并且只有当选择器是单个类名时才有效。 当一个类名组成另一个类名时,CSS 模块将导出本地类的两个类名。这可能会累加到多个类名。 也可以使用组合来组合多个类 composes: classNameA classNameB;

在 Header.module.css 中,让.title 继承.back 。

Header.module.css

css
.back {
	background-color: blue;
}

.title {
	composes: back;
	color: green;
}

Header.js

jsx
import styles from "./Header.module.css";

export default function Header() {
	return <h2 className={styles.title}>Header 组件</h2>;
}

编译后 CSS

css
._src_Header_module__back {
	background-color: blue;
}

._src_Header_module__title {
	color: green;
}

HTML

html
<h2 class="_src_Header_module__title _src_Header_module__back">Header 组件</h2>

注意:

当在使用composes组合语句时,类上将附加上伪类选择器

在下面的例子中,otherClassName也将被赋予定义在className上的:hover伪类。

css
.className {
	color: green;
}

.className:hover {
	color: red;
}

.otherClassName {
	composes: className;
	background: black;
}

otherClassName above is the same as defining:

css
.otherClassName {
  color: green;
  background: black;
}

.otherClassName:hover {
  color: red;
}

继承其他模块(Class复用)

选择器也可以继承其他 CSS 文件里面的规则。

other.module.css

css
.other {
	background-color: chartreuse;
}

Header.module.css

css
.title {
	composes: other from "./other.module.css";
	color: green;
}

注意:

导入的类名需要和被导入文件中的类名相同

编译之后的效果和 composes 同一个文件中的 class 效果相同

组合不应该形成循环依赖。 否则,规则的属性是否覆盖组合规则的属性是未定义的。 模块系统可能会发出一个错误

变量

CSS Modules 支持使用变量,不过需要安装 PostCSS 和 postcss-modules-values

安装

bash
npm install --save postcss-loader postcss-modules-values

webpack.config.js

text
{
    loader: "postcss-loader",
    "options": {
        plugins: [
            require('postcss-modules-values'),
        ]
    }
}

使用

text
@value color: #8A469B;

.header {
	background: color;
}

.footer {
	// 类组合
	composes: header;
	color: #FFF;
}

推荐原生的css变量

在ts中同样使用,

因为在这里我们用的是TypeScript,所以可以用typings-for-css-modules-loader这个包, 这个包也可以替代css-loader的功能,此外这个包还能根据.scss文件里面的类名自动生成对应的.d.ts文件:

bash
npm install -D typings-for-css-modules-loader

配置webpack 这个配置接非常简单了,因为要用typings-for-css-modules-loader替代css-loader的功能,所以直接替换即可,将前面sass的配置修改为如下:

配置后在页面中引入,引入可能会提示找不到该模块,出现这个问题的原因是:

因为.scss文件中并没有类似export这样的关键词用于导出一个模块,所以也就导致报错找不到模块,这个问题需要通过ts的模块声明解决。 (declare module)

发现是ts的模块机制,所以需要声明模块,创建一个*.d.ts的文件

style为自定义名,随便取

ts
declare module '*.css' {
	const style: any;
	export default style;
}

这样就不会报错了

CSS Modules 使用技巧

CSS Modules 是对现有的 CSS 做减法。为了追求简单可控,作者建议遵循如下原则:

不使用选择器,只使用 class 名来定义样式 不层叠多个 class,只使用一个 class 把所有样式定义好 不嵌套 使用 composes 组合来实现复用

如果我在 style 文件中使用了 id 选择器,伪类,标签选择器等呢?

没问题,所有这些选择器将不被转换,原封不动的出现在编译后的 css 中。也就是说 CSS Modules 只会转换 class 名相关样式

css Module里less里嵌套css,使用的时候,style.xxx不用嵌套吗?

在CSS Modules中,使用Less编译器编译并转换成模块化CSS后,你不需要在JavaScript中通过style.xxx 的方式进行嵌套引用。当你在Less文件中使用嵌套语法编写样式时,编译过程会自动将这些嵌套转换为扁平化的类名,并且生成一个映射对象。

例如,在Less文件(假设为styles.module.less)中:

less
.container {
	color: red;

	.item {
		background-color: blue;
	}
}

编译后,对应的CSS Modules JavaScript对象可能是这样的:

text
import styles from './styles.module.less';

// styles对象可能类似于:
{
	container: 'container__unique-hash',
	item:'container__unique-hash-item__unique-hash'
}

然后在React组件或其他JavaScript代码中引用时,直接使用生成的类名即可:

jsx
<div className={styles.container}>
	<div className={styles.item}>This is an item</div>
</div>

注意这里的styles.item并不是styles.container.item,因为在实际应用中,CSS Modules已经将Less中的嵌套结构扁平化了。

Contributors

作者:Long Mo
字数统计:2.1k 字
阅读时长:7 分钟
Long Mo
文章作者:Long Mo
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Longmo Docs