浅析如何给css加上模块的功能以及CSS Modules用法介绍
一、如何给 css 加上模块的功能呢?
我们知道,浏览器里的 JS 之前没有模块的概念,都是通过不同的全局变量(命名空间)来隔离,后来出现了 AMD、CMD、CommonJS、ESM 等规范。
通过这些模块规范组织的 JS 代码经过编译打包之后,运行时依然会有模块级别的作用域隔离(通过函数作用域来实现的)。
组件就可以放在不同的模块中,来实现不同组件的 JS 的作用域隔离。
但是组件除了 JS 还有 CSS 呀,CSS 却一直没有模块隔离的规范,如何给 css 加上模块的功能呢?
有人说 CSS 不是有 @import 吗?那个只是把不同的 CSS 文件合并到一起,并不会做不同 CSS 的隔离。
CSS 的隔离主要有两类方案,一类是运行时的通过命名区分,一类是编译时的自动转换 CSS,添加上模块唯一标识。
1、运行时的方案最典型的就是 BEM,它是通过 .block__element--modifier 这种命名规范来实现的样式隔离,不同的组件有不同的 blockName,只要按照这个规范来写 CSS,是能保证样式不冲突的。
但是这种方案毕竟不是强制的,还是有样式冲突的隐患。
2、编译时的方案有两种,一种是 scoped,一种是 css modules。
(1)scoped 是 vue-loader 支持的方案,它是通过编译的方式在元素上添加了 data-xxx 的属性,然后给 css 选择器加上[data-xxx] 的属性选择器的方式实现 css 的样式隔离。
通过给 css 添加一个全局唯一的属性选择器来限制 css 只能在这个范围生效,也就是 scoped 的意思。
(2)css-modules 是 css-loader 支持的方案,在 vue、react 中都可以用,它是通过编译的方式修改选择器名字为全局唯一的方式来实现 css 的样式隔离。
<style module>
.guang {
color: red;
}
</style>
<template>
<p :class="$style.guang">hi</p>
</template>
// 会被编译成:
<style module>
._1yZGjg0pYkMbaHPr4wT6P__1 {
color: red;
}
</style>
<template>
<p class="_1yZGjg0pYkMbaHPr4wT6P__1">hi</p>
</template>
和 scoped 方案的区别是 css-modules 修改的是选择器名字,而且因为名字是编译生成的,所以组件里是通过 style.xx 的方式来写选择器名。
3、两种方案都是通过编译实现的,但是开发者的使用感受还是不太一样的:
scoped 的方案是添加的 data-xxx 属性选择器,因为 data-xx 是编译时自动生成和添加的,开发者感受不到。
css-modules 的方案是修改 class、id 等选择器的名字,那组件里就要通过 styles.xx 的方式引用这些编译后的名字,开发者是能感受到的。但是也有好处,配合编辑器可以做到智能提示。
4、此外,除了 css 本身的运行时、编译时方案,还可以通过 JS 来组织 css,利用 JS 的作用域来实现 css 隔离,这种是 css-in-js 的方案。
5、css-modules 的编译时方案是用的最多的,vue、react 都可以用,那它是怎么实现的呢?
打开 css-loader 的 package.json,你会发现依赖了 postcss(css 的编译工具,类似编译 js 的babel):
其中这四个 postcss-modules 开头的插件就是实现 css-modules 的核心代码。
这四个插件里,实现作用域隔离的是 postcss-modules-scope,其他的插件不是最重要的,比如 postcss-modules-values 只是实现变量功能的。
所以说,我们只要能实现 postcss-modules-scope 插件,就能搞懂 css-modules 的实现原理了。具体实现代码可看这篇文章:https://mp.weixin.qq.com/s/3xhNcfI4pTm0n-naSsOBwg
二、CSS Modules 用法
学过网页开发就会知道,CSS 不能算编程语言,只是网页样式的一种描述方法。为了让 CSS 也能适用软件工程方法,程序员想了各种办法,让它变得像一门编程语言。从最早的Less、SASS,到后来的 PostCSS,再到最近的 CSS in JS,都是为了解决这个问题。
而 CSS Modules 有所不同。它不是将 CSS 改造成编程语言,而是功能很单纯,只加入了局部作用域和模块依赖,这恰恰是网页组件最急需的功能。
1、局部作用域:默认就是局部作用域
import style from './App.css';
<h1 className={style.title}>Hello World</h1>
其实就是需要用导入的 style.title 去设置 class 类名即可
2、全局作用域
CSS Modules 允许使用:global(.className)
的语法,声明一个全局规则。凡是这样声明的class
,都不会被编译成哈希字符串。
import styles from './App.css';
<h1 className="title">Hello World</h1>
这个就可以导入 styles 然后直接写 title
3、可定制hash类名
4、class组合
在 CSS Modules 中,一个选择器可以继承另一个选择器的规则,这称为"组合"("composition")
//在App.css中,让.title继承.className
.className {
background-color: blue;
}
.title {
composes: className;
color: red;
}
5、选择器也可以继承其他CSS文件里面的规则
// another.css
.className {
background-color: blue;
}
// App.css可以继承another.css里面的规则。
.title {
composes: className from './another.css';
color: red;
}
6、输入变量
CSS Modules 支持使用变量,不过需要安装 PostCSS 和 postcss-modules-values
npm install --save postcss-loader postcss-modules-values
把postcss-loader
加入webpack.config.js
// 接着,在colors.css里面定义变量
@value blue: #0c77f8;
@value red: #ff0000;
@value green: #aaf200;
// App.css可以引用这些变量
@value colors: "./colors.css";
@value blue, red, green from colors;
.title {
color: red;
background-color: blue;
}
具体使用详见《阮一峰:CSS Modules 用法教程》https://www.ruanyifeng.com/blog/2016/06/css_modules.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2019-07-29 Vue优化:常见会导致内存泄漏问题及优化
2019-07-29 vue自定义指令导致的内存泄漏问题解决
2017-07-29 小知识随手记(四)