React中样式处理

Posted by Juan on 2017-01-10

简单介绍CSS Modules、React-CSS-Modules、classnames概念及用法

CSS Modules

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default.

CSS Modules实现了css模块化,我们可以像引入js文件一样来引入css文件,所有样式都是局部化的,不用担心命名冲突,全局污染的问题,此外,还能通过CSS Modules提供的组合方法达到对样式的复用。CSS Modules多应用于提取公共组件或业务公共组件场景中

CSS Modules的配置

webpack css-loader已经内置CSS Modules功能,启用css Modules代码如下:

1
2
3
4
5
6
7
8
9
module: {
loaders: [
// ...
{
test: /\.css$/,
loader: "style-loader!css-loader?modules&localIdentName=[path][name]---[local]---[hash:base64:5]"
},
]
}

其中,关键词modules,表示启用CSS Modules 功能,localIdentName用于定制生成样式的命名规则

CSS Modules的使用

在React环境中,CSS Modules 看起来是这样子的:

1
2
3
4
5
6
7
8
9
10
11
12
import React, { Component } from 'react';
import styles from './table.css';
export default class Table extends Component {
render () {
return <div className={styles.table}>
<div className={styles.row}>
<div className={styles.cell}>A0</div>
<div styleName='cell'>B0</div>
</div>
</div>;
}
}

组件渲染出来后会生成类似于这样的一个标记:

1
2
3
4
5
6
<div class="table__table___32osj">
<div class="table__row___2w27N">
<div class="table__cell___2w27N">A0</div>
<div class="table__cell___1oVw5">B0</div>
</div>
</div>

同时也会生成一个以hash映射形式来匹配相应的类名的table.css.js文件:

1
2
3
4
5
module.exports = {
"table": "table__table___32osj"
"row": "table__row___2w27N",
"cell": "table__cell___2w27N"
}

此外,CSS Modules还提供了compose组合方法来处理样式复用,示例代码如下:

1
2
3
4
5
6
7
8
9
10
.className {
color: green;
background: red;
font-size: 12px;
}

.otherClassName {
composes: className;
color: yellow;
}

CSS Modules优点

  • 所有样式都是局部化的,解决了命名冲突和全局污染问题
  • class名的生成规则配置灵活
  • 引入组件,只需引用组件的js文件,不需要额外去引入css文件
  • 依然是css,学习成本几乎为零

目前存在的问题

  • 必须使用驼峰式的命名(若以‘-’命名,则需要通过styles[]这种形式关联)
  • 需要频繁的输入style.**
  • 引用一个未定义的 CSS 模块时解析结果为 undefined ,但并无相关警告提示

React CSS Modules

React CSS Modules implement automatic mapping of CSS modules. Every CSS class is assigned a local-scoped identifier with a global unique name. CSS Modules enable a modular and reusable CSS!

React CSS Modules实现了自动化映射 CSS modules,完美得解决了上述问题,它的原理是扩展了render方法,根据styleName的值去在关联的styles对象中查找相应的css-module,并给每个 CSS 类赋予一个带有全局唯一名字的本地标识符的类名。

React CSS Modules的使用

使用React css Modules上述例子可以简化为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import CSSModules from 'react-css-modules'
import styles from './table.css';

class Table extends React.Component {
render () {
return <div styleName="root">
<div styleName="row">
<div styleName="cell">A0</div>
</div>
</div>;
}
}
export default CSSModules(Table, styles);

Options配置

React CSS Modules提供了options作为第三个参数来对CSS Modules的配置

  • allowMultiple 默认为false
  • errorWhenNotFound 默认为true
    1
    CSSModules(Component, styles, options); // options = { allowMultiple: false, errorWhenNotFound: true }

React CSS Module的优点

  • 我们不用再关注是否使用驼峰来命名class名
  • 不用每次使用CSS Modules都用styles.**来关联对象
  • 通过<div className="global-css" styleName="local-css"></div>明显区分全局CSS和CSS Modules
  • 当styleName关联了一个undefined CSS Modules时,会得到一个警告信息(errorWhenNotFound option)
  • 强制使用一个CSS Modules, 推崇使用CSS Modules 的样式组合(allowMultiple option)

classnames

A simple javascript utility for conditionally joining classNames together.

在业务开发中,少不了对类的操作,以button组件为例,如果不使用classnames库,需要这样处理动态类名

1
2
3
4
5
6
7
8
9
10
11
import React,{ Component } from 'react';

class Button extends Component {
// ...
render () {
let btnClass = 'btn';
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
}
};

使用了classnames库,代码简化如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
import React,{ Component } from 'react';

class Button extends Component {
// ...
render () {
const btnClass = classNames({
'btn': true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered,
});
return <button className={btnClass}>{this.props.label}</button>;
}
};

至此,有了CSS Modules, React-CSS-Modules,classnames,通过简单配置,我们就可以把精力集中在编写css样式上了,而无须为类名的命名感到头疼,也不用频繁地使用styles.**来关联样式,当styleName关联了一个undefiend CSS Modules时,会得到一个警告信息,而不用去查找原因去看DOM结点,再加之有了classnames的动态操作样式的助攻,我们的代码将会变得更加简明和清晰!

参考文档: