[转] 使用babel-plugin-react-css-modules简化CSS Modules的使用
在我们的产品中,均使用CSS Modules来作为样式解决方案,大致的代码是这样的:
import React from 'react';
import styles from './table.css';
export default class Table extends React.Component {
render () {
return <div className={styles.table}>
<div className={styles.row}>
<div className={styles.cell}>A0</div>
<div className={styles.cell}>B0</div>
</div>
</div>;
}
}
但这里显然存在一些细节上的麻烦:
- 引入样式时额外增加了一个
styles
变量 - 需要不断写
styles.xxx
,重复代码
babel-plugin-react-css-modules
插件可以一定程度上缓解这些问题,使代码变为:
import React from 'react';
import './table.css';
export default class Table extends React.Component {
render () {
return <div styleName='table'>
<div styleName='row'>
<div styleName='cell'>A0</div>
<div styleName='cell'>B0</div>
</div>
</div>;
}
}
难点
- 我们的产品使用LESS而非原生的CSS来编写样式
- 为了生成的类名更漂亮,我们使用CSS Modules用了一个自定义的
getLocalIdent
实现 - 与webpack的整合可能存在一些难点
解决方案
安装依赖
npm i --save babel-plugin-react-css-modules
npm i --save-dev postcss-less
需要注意的是,babel-plugin-react-css-modules
有一个运行时依赖,所以用--save
安装比较好。而postcss-less
则用于解析LESS的语法
调整构建配置
因为有一个自定义的生成类名的函数,所以原有的.babelrc
的JSON格式已经不够了(无法表达函数),因此我们要把.babelrc
的配置移到babel-loader
的options
里去
在完成移动后,再向其中添加babel-plugin-react-css-modules
这一插件,在这个过程中将生成类名的函数抽象出来:
const generateScopedName = (name, filename) => {
const hash = hasha(filename + name, {algorithm: 'md5'});
const basename = path.basename(filename, '.less');
return `${dashCase(basename)}-${name}-${hash.slice(0, 5)}`;
};
exports.babel = {
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [
// ...预置集
],
plugins: [
// ...其它插件
[
'react-css-modules',
{
context: path.join(__dirname, '..'),
exclude: 'node_modules',
filetypes: {
'.less': {
syntax: 'postcss-less'
}
},
generateScopedName: generateScopedName
}
]
]
}
};
以上文件为webpack/loaders.js
,相关的配置基本不用修改,原样使用就行。如果这些代码的位置不同,将其中的context
配置修改一下,对应至项目根目录就行。
然后调整一下CSS Modules相关的loader的配置,复用generateScopedName
函数:
exports.cssModules = {
loader: 'css-loader',
options: {
sourceMap: development,
modules: true,
importLoaders: true,
camelCase: 'dashes',
getLocalIdent({resourcePath}, localIdentName, localName) {
return generateScopedName(localName, resourcePath);
}
}
};
修改一些源码
需要特别注意:所有从.js
中引用.less
的代码,都不可以依赖webpack的resolve.modules
配置,只能写相对路径了。即原来写import 'common/less/global.less'
要改成import './common/less/global.less'
随后按着喜好将已经用了className
的地方慢慢改成styleName
就行。