css modules 实际使用疑问(与 公共组件、less、ts、webpack 一起使用)
我一直是 BEM 党,相信很多 BEM 党和我一样,在了解 css modules 时会有一些疑问:
这玩意在公共组件中用了,别人怎么覆盖样式?
本文包含:如何覆盖使用了 css modules
的公共组件样式;与 less
一起使用;与 ts
一起使用;与 webpack
一起使用(已经不需要配2条rule
了)
一、如何覆盖用 css modules
的公共组件的样式?
如果公共组件使用了css modules
,组件的类名就会随机变化,外部无法通过类名覆盖,如何解决该问题?
stackoverflow 有相同问题:stackoverflow.com/questions/3…
问题描述:大致就是 React Toolbox 组件库,用的 css modules,如何覆盖它的样式?
回答里,贴了组件库官方提供的方法:github.com/react-toolb…
每个组件接受外部传 theme 参数(css module 对象),应用在dom上
css modules
本身就不允许通过类名从外部覆盖样式,因为类名会动态变化(设计如此)
要覆盖,需要公共组件维护者暴露api(参数)。或者要覆盖的样式不深的话,直接给组件第一层元素传一个 className 修改了
二、less嵌套 与 css modules 一起使用
stackoverflow 也有相同问题:stackoverflow.com/questions/3…
非常赞同提问者说的,css-modules 文档简直稀少
css modules
提供 compose
属性,来组合样式,尽量不用选择器:
.common { /* font-sizes, padding, border-radius */ }
.normal { composes: common; /* blue color, light blue background */ }
.error { composes: common; /* red color, light red background */
但如果之前用过 less
+ BEM规范
,更喜欢使用 less嵌套
来实现:
.common {
/* font-sizes, padding, border-radius */
&.normal { /* blue color, light blue background */ }
&.error { /* red color, light red background */ }
}
用了 css modules
后,还能实现 less
的嵌套功能吗?其实启用了 css modules
,还是可以嵌套的
2.1 less嵌套
+ css modules
先来看一下,less嵌套
在启用 css modules
后,会转换成什么:
首先明白,css modules
并不是 less
本身的功能,.module.less
文件还是先被 less-loader
正常处理,less-loader
的输出才交由 css-loader
(支持 css modules
)处理
对于 less
中的嵌套:
.text {
font-size: 30px;
&.normal {
color: green;
}
&.warning {
color: orange;
}
}
less-loader
先转换为选择器写法:
.text {
font-size: 30px;
}
.text.normal {
color: green;
}
.text.warning {
color: orange;
}
css-loader
再转换:
._3llRUjrJsZwPb0q26xNdQw {
font-size: 30px;
}
._3llRUjrJsZwPb0q26xNdQw._2JtRNLiJkzX_XvYwpKn-dI {
color: green;
}
._3llRUjrJsZwPb0q26xNdQw.Mv2HDRRET-VJjfPOWPXWc {
color: green;
}
dom
中这样使用:
import classNames from "classnames";
import styles from "./index.module.less";
<div className={classNames(styles.text, styles.normal)}>
这是用 css modules 的页面
</div>
<div className={classNames(styles.text, styles.warning)}>
就不需要定义 BEM 的 PREFIX 咯
</div>
注意:.text
、.normal
、.warning
都需要从 import
的 styles
中获取,这样 css-loader
才能在打包时将 dom
上的类名 与 css中的样式名 对应
2.2 用 :global
保留嵌套的方式行不通
上文贴的 stackoverflow 里最高赞回答,说可以用css-modules
的:global
来保留less嵌套
:
# 该方法是不行的!
.common {
/* font-sizes, padding, border-radius */
:global {
&.normal { /* blue color, light blue background */ }
&.error { /* red color, light red background */ }
}
}
使用 styles.common 输出:
# 该方法是不行的!
.ramdomStrings {
/* I'm not sure if this exists, but it doesn't matter */
}
.ramdomStrings.normal { /* blue color, light blue background */ }
.randomStrings.error { /* red color, light red background */ }
以此来保留.normal
、.error
不被转成随机字符串(其实转不转区别也不大)
实际尝试,会报错:Missing whitespace after :global
该回答下面的评论也有说相同报错的:
即这样:global
+less &.嵌套
后,less-loader
输出的内容css modules
会报错
三、 webpack
启用 css modules
css-loader
提供了css modules
:github.com/webpack-con…
loader:
webpack 中,每种非 js 资源(css、图片等)都需要对应的 loader,来将它们转换成 js 模块
这样才能在使用
import "xxx"
、@import(...)
、url()
时,webpack 正确识别
实际上现在要启用 css modules
,已经不需要修改什么 webpack
配置了
很多老文章会说,需要将处理样式文件的规则,拆分成两个:一个规则匹配.module.css
文件(开启 modules),另一个规则匹配.css
文件(关闭 modules)。类似下面这样的做法:
# 该配置已经老了,现在不需要2条规则!
# 处理 .less 但不是 .module.less 的文件,不使用 css modules
{
test: /.(less|css)$/,
exclude: /.module.(less|css)$/,
# 提供一个函数,来返回规则内容;以免大段落的复制粘贴到两处
use: getStyleRule(false),
},
# 仅处理 .module.less 的文件,使用 css modules
{
test: /.module.(less|css)$/,
use: getStyleRule(true),
},
现在 css-loader 已经支持一条规则统一处理这种情况了:github.com/webpack-con…
css-loader
的 modules
配置项,默认值为 undefined
undefined
:对(传给loader
的)所有与/.module.\w+$/i
或/.icss.\w+$/i
相匹配的文件启用css modules
默认值,已经完全满足我们的需求了!
反而是,手动修改成 true
或 false
了,才需要写2条规则
false
:对所有文件不启用css modules
true
:对所有文件启用css modules
# 都用默认,就已经支持 css modules 了
{
test: /\.(less|css)$/,
use: ["style-loader", "css-loader", "less-loader"],
},
四、css-modules
和 typescript
一起使用
如果使用ts
,import .module.less
,会报错:Cannot find module './index.module.less' or its corresponding type declarations.
import styles from "./index.module.less";
经常看到这个报错,ts
找不到这种模块的类型声明,怎么解决呢?
当然可以直接在 .d.ts
里 declare
一个导出 any
类型的 *.module.less
模块
但还有更好的方案,可以让使用 styles.xxx
时推断出可用的属性!
4.1 typescript-plugin-css-modules
ts插件typescript-plugin-css-modules
就提供了该功能,但用起来真的这么丝滑吗?
因为上文我们有提到当 less
与 css modules
一起使用时,这个工具也能识别less嵌套
吗?
试试:
yarn add -D typescript-plugin-css-modules
- 配置
tsconfig.json
,使用该插件 - 该插件有个注意事项,
vs code
使用的ts
版本必须切换成工作目录的版本
启用后,嵌套也能识别:
牛啊牛啊🐂!
转载自:https://juejin.cn/post/7067071342343880712