likes
comments
collection
share

css modules 实际使用疑问(与 公共组件、less、ts、webpack 一起使用)

作者站长头像
站长
· 阅读数 19

我一直是 BEM 党,相信很多 BEM 党和我一样,在了解 css modules 时会有一些疑问:

这玩意在公共组件中用了,别人怎么覆盖样式?

本文包含:如何覆盖使用了 css modules 的公共组件样式;与 less 一起使用;与 ts 一起使用;与 webpack 一起使用(已经不需要配2条rule了)

本文示例:risuslumber.com/planet/inde…

一、如何覆盖用 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 都需要从 importstyles 中获取,这样 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

该回答下面的评论也有说相同报错的:

 css modules 实际使用疑问(与 公共组件、less、ts、webpack 一起使用)

即这样:global+less &.嵌套后,less-loader输出的内容css modules会报错

三、 webpack 启用 css modules

css-loader提供了css modulesgithub.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-loadermodules 配置项,默认值为 undefined

  • undefined:对(传给loader的)所有与 /.module.\w+$/i/.icss.\w+$/i 相匹配的文件启用css modules

默认值,已经完全满足我们的需求了!

反而是,手动修改成 truefalse 了,才需要写2条规则

  • false:对所有文件不启用css modules
  • true:对所有文件启用css modules
# 都用默认,就已经支持 css modules 了

{
  test: /\.(less|css)$/,
  use: ["style-loader", "css-loader", "less-loader"],
},

四、css-modulestypescript 一起使用

如果使用tsimport .module.less,会报错:Cannot find module './index.module.less' or its corresponding type declarations.

import styles from "./index.module.less";

经常看到这个报错,ts找不到这种模块的类型声明,怎么解决呢?

当然可以直接在 .d.tsdeclare 一个导出 any 类型的 *.module.less 模块

但还有更好的方案,可以让使用 styles.xxx 时推断出可用的属性!

 

4.1 typescript-plugin-css-modules

ts插件typescript-plugin-css-modules就提供了该功能,但用起来真的这么丝滑吗?

因为上文我们有提到当 lesscss modules 一起使用时,这个工具也能识别less嵌套吗?

试试:

  1. yarn add -D typescript-plugin-css-modules
  2. 配置tsconfig.json,使用该插件
  3. 该插件有个注意事项,vs code 使用的 ts 版本必须切换成工作目录的版本

启用后,嵌套也能识别:

 css modules 实际使用疑问(与 公共组件、less、ts、webpack 一起使用)

牛啊牛啊🐂!

转载自:https://juejin.cn/post/7067071342343880712
评论
请登录