使用babel-plugin-react-css-modules简化React中CSS模块的使用

前言

CSS 是前端领域中进化最慢的一块。在 React 中使用 css 是一件很痛苦的事情。从 从原生 css 到 css modulesCSS in JS。各种解决方案层出不穷。
总结起来主要有两类:

  • 彻底抛弃 CSS,使用 JS 或 JSON 来写样式,如时下的新宠 styled-components (github上两万多star),还有 styled-jsxreact-style等属于CSS in JS这一类。优点是能给 CSS 提供 JS 同样强大的模块化能力。
  • 依旧使用 CSS,但使用 JS 来管理样式依赖,代表是 CSS ModulesCSS Modules 能最大化地结合现有 CSS 生态(预处理器/后处理器等)和 JS 模块化能力,几乎零学习成本。只要你使用 Webpack,可以在任何项目中使用。是目前最好的 CSS 模块化解决方案。这篇文章要介绍的babel-plugin-react-css-modules就是一个CSS模块化的轻量级解决方案。

为什么要有CSS Modules

请看这里

CSS Modules 模块化方案

CSS Modules 并不是React专用解决方法,适用于所有使用 webpack 等打包工具的开发环境。以 webpack 为例,在 css-loader 的 options 里打开modules:true 选项即可使用 Css Modules
一般配置如下

1
2
3
4
5
6
7
8
{
loader: "css-loader",
options: {
importLoaders: 1,
modules: true,
localIdentName: "[local]___[hash:base64:5]" // 为了生成类名不是纯随机
},
}

使用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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>
</div>;
}
}
// 渲染结果:
<div class="table__table___32osj">
<div class="table__row___2w27N">
</div>
</div>

像这样,每次都要用style.xxx这种方式来写,如果有多个class还需通过字符串模板拼接,还可一使用classnames动态控制样式的显示。

1
2
3
4
5
6
7
npm install classnames

import styles from './styles.css'
import classNames from 'classnames'

<div className={ styles.theTitle }>something</div>
<div className={ classNames(styles.theTitle, styles.title) }>something</div>

能不能更简单??? 终于到babel-plugin-react-css-modules出场了。

babel-plugin-react-css-modules

先来个对比,className换成styleName就ok了,其他跟使用className一样,有没有很简单

1
2
3
4
5
6
7
8
9
// css-modules
import styles from './styles.css'

<div className={ styles.theTitle }>something</div>

// babel-plugin-react-css-modules
import './styles.css'

<div styleName="the-title">something</div>

官方链接最早是react-css-modules这个插件,作者后来又开发了这个使用起来更为方便的新插件。配置起来也很简单。

安装依赖

需要注意的是,babel-plugin-react-css-modules有一个运行时依赖,所以用–save安装比较好

1
npm i --save babel-plugin-react-css-modules

配置

配置主要注意一下两点:

  • 配置项中的generateScopedName的作用和css-loader中的localIdentName是一样的,都是设置编译之后的类名的格式。generateScopedNamelocalIdentName必须要配置,并且值必须相同。否则即使编译成功,不报错,也还是无法达到预期效果。
  • context要和webpack中context的配置一直,默认是项目根目录
1
2
3
4
5
6
7
8
[
'babel-plugin-react-css-modules',
{
context: path.join(__dirname, '.'),
exclude: 'node_modules',
generateScopedName: generateScopedName
}
]

然后就可以愉快的使用了

支持SCSS

安装依赖

1
2
3
4
5
6
7
8
// 该插件让‘babel-plugin-react-css-modules’支持‘scss’
npm install postcss-scss --save-dev

// sass和scss的依赖库
npm install node-sass --save-dev

// sass-loader就不需要说明了吧
npm install sass-loader --save-dev

配置

sass-loader的配置很简单,就不说了,重点看下babel-plugin-react-css-modules的配置

1
2
3
4
5
6
7
8
9
10
["babel-plugin-react-css-modules", {
context: path.join(__dirname, '.'),
exclude: "node_modules",
filetypes: {
".scss": {
"syntax": "postcss-scss"
}
},
generateScopedName: generateScopedName
}],

filetypes这里是让这个插件支持scss。这里其实还可以添加插件,具体可以在官方文件中查看。

需要特别注意:所有从.js中引用.scss的代码,都不可以依赖webpack的resolve.modules配置,只能写相对路径了。即原来写import 'common/scss/global.scss'要改成import './common/scss/global.scss'

补充

styleName出场了,className干嘛?? 依然可以通过className使用全局样式,babel-plugin-react-css-modules会自动合并styleNameclassName