使用babel-plugin-react-cssmodules替代react-css-modules

Why

在更新 React 至 16.6.*后,使用 react-css-modules 的项目会出现以下 Warning

1
Expected instance props to match memoized props before componentDidUpdate. This is likely due to a bug in React. Please file an issue.

主要是因为在 react-cssmodules 中,重写了 this.props,详见issues

我们访问 react-css-modules 的项目,发现官方推荐了 babel-plugin-react-css-modules 作为替代品,其有以下优势:

  1. 将处理过程交给 babel 而不是 runtime, 提升 runtime 性能
  2. 不用频繁的调用 cssmodules 高阶组件
  3. 统一处理 options 等

替换过程

官方案例很简单,但是我们替换的过程中还是遇到了挺多的问题。主要包含以下几个步骤:

1. 安装 babel-plugin-react-css-modules

不赘述

2. 升级 babel 7

升级 babel 为@babel@7.1.6

对应的 plugin 也需要进行升级, 官方有介绍相应的替换

1
2
3
4
5
6
7
8
"@babel/core": "^7.1.6",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.1.6",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.1.0",
"@babel/preset-env": "^7.1.6",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",

3. 格式化 stylus 文件

我们需要对 stylus 进行统一的格式化处理,防止 babel 对语法校验出错,由于文件较多,我们使用 stylus-supremacy 进行处理,使用 node 写一个批处理文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const stylusSupremacy = require("stylus-supremacy");
const fs = require("fs");
const path = require("path");
const formattingOptions = {
insertColons: false,
insertSemicolons: false,
insertBraces: false
};
function formatFile(path) {
const content = fs.readFileSync(path, "utf8");
const result = stylusSupremacy.format(content, formattingOptions);
fs.writeFileSync(path, result, "utf8");
console.log("format 成功: " + path);
}
...

4. 配置 css-loaders

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const cssRegex = /\.css$/;
const cssModuleRegex = /\.cssmodule\.css$/;
const stylRegex = /\.styl$/;
const stylModuleRegex = /\.cssmodule\.styl$/;
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
require.resolve("style-loader"),
{
loader: require.resolve("css-loader"),
options: cssOptions
}
];
if (preProcessor) {
loaders.push(require.resolve(preProcessor));
}
return loaders;
};
// webpack配置,添加getLocalIdent方法
rules: [
{
test: stylRegex,
exclude: stylModuleRegex,
use: getStyleLoaders({ importLoaders: 2 }, "stylus-loader")
},
{
test: stylModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
modules: true,
camelCase: "dashes",
getLocalIdent({ resourcePath }, localIdentName, localName) {
return generateScope(localName, resourcePath);
}
},
"stylus-loader"
)
}
];

5. 配置 babel-plugin-react-css-modules

generateScopedName 需要与 cssloader 处理的结果一致,否则将会失效。

同时,styl 文件需要 sugarss 进行处理,记得安装

1
2
3
4
5
6
7
8
9
10
[
"react-css-modules",
{
generateScopedName: localIdentName,
filetypes: { ".styl": { syntax: "sugarss" } },
webpackHotModuleReloading: true, // 支持热加载
handleMissingStyleName: "warn", // 没有时进行警告
exclude: ".css$"
}
];

6. 运行

删除之前的 react-css-modules

问题

  • 同时处理多个文件时报错

在文件中引用多个文件,会报错

1
2
import './common.css'
import './index.cssmodule.styl'

解决方案: 添加配置: exclude: “.css\$”