CSS Modules
在React中写样式有多种方式,比较常见的有 CSS modules
,这种方法将css样式和组件放在一起,然后组件中直接应用,目录结构:
|—src
| |_components
| |_ButtonComponent
| |_Button.jsx
| |_button.sass
具体示例: css mudules in react
可以看出通过模块应用的样式都是通过这样的形式:
# 1.先引入对应模块的样式
import styles from './GlobalSelectors.css';
# 2.使用 className={styles.container} 这种形式表示模块class名
# 而 className="text-left" 这种形式则表示全局下的选择器
export default class GlobalSelectors extends Component {
render() {
return (
<div className={ styles.container }>
<p className="text-left">Global Selectors</p>
</div>
);
}
}
// css文件为 GlobalSelectors.css
.container {
border-width: 2px;
border-style: solid;
border-color: brown;
padding: 0 20px;
margin: 0 6px;
max-width: 400px;
}
# ':global' 表示该类为全局作用域下的
.container :global .text-left {
float: left
}
css modules本身需要css-loader来配合,这可能会出现的缺点:
- 必须使用
camelCase
来命名 css class names - 当引入到
className
中时必须要使用 styles 对象 - CSS modules 和 全局css类混合在一起会很难管理
- 引用没用定义的CSS modules不会出现警告
而react css Modules组件通过 styleName
将自动的加载CSS MODULES.
react-css-modules
使用react-css-modules将解决上面css modules的问题,例如:
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './tabel.sass'
class Table extends React.Component {
render() {
return (
# className 表示全局类名
# styleName 表示模块类名
<div styleName="table" className="tabel--info">
<div styleName="row">
<div styleName="cell">A0</div>
<div styleName="cell">B0</div>
</div>
</div>
);
}
}
# CSSModules 对组件进行修饰
export default CSSModules(Table, styles);
下面谈具体实现步骤和注意事项
1.安装
通过npm安装:
npm install --save react-css-modules
2.webpack配置
这个包需要用到 style-loader | css-loader
1.对css文件进行配置
对于开发阶段:
# 注意 loaders 为复数
{
test: /\.css$/,
loaders: [
'style?sourceMap',
'css?modues&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
]
}
对于产品阶段: 使用2.x版本 extract-text-webpack-plugin
npm install --save-dev extract-text-webpack-plugin@2
npm install --save-dev resolve-url-loader post-loader
// webpack.config.js file
var ExtractTextPlugin = require('extract-text-webpack-plugin');
# 注意 loader 为单数
{
test: /\.css$/,
loader: ExtractTextPlugin({
notExtractLoader: 'style-loader',
loader: 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base4:5]!resolve-url!postcss'
})
}
# 配置ExtractTextPlugin
plugins: [
// ...
new ExtractTextPlugin({
filename: 'app.css',
allChunks: true
})
]
2.对于使用sass或其它预处理器
安装需要的加载器:
npm install --save-dev resolve-url-loader sass-loader node-sass
// 不使用sourceMap
{
test: /\.sass$/,
loaders: [
'style',
'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
'resolve-url',
'sass'
]
}
// 使用sourceMap
{
test: /\.sass$/,
loaders: [
'style?sourceMap',
'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
'resolve-url',
'sass?sourceMap'
]
}
当然产品阶段的配置也类似
3.使用 'styles' 重写组件样式
比如:
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';
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>;
}
}
export default CSSModules(Table, styles);
这是常见写法,如果要重写styles中样式, 可以在组件中使用 styles
来重写组件样式:
# 引入自定义样式
import customStyles from './table-custom-styles.css';
# 使用 styles属性来重写之前的样式
<Table styles={customStyles} />
4. 循环和子组件
styleName 不能用来修饰组件中的子组件,比如:
import React from 'react';
import CSSModules from 'react-css-modules';
import List from './List';
import styles from './table.css';
class CustomList extends React.Component {
render () {
let itemTemplate;
# 使用styleName 来修饰CustomList组件的子组件List
# 这是不允许的
itemTemplate = (name) => {
return <li styleName='item-template'>{name}</li>;
};
return <List itemTemplate={itemTemplate} />;
}
}
export default CSSModules(CustomList, styles);
可以通过下面2种方法来改写:
方法1:使用 styles 属性
import React from 'react';
import CSSModules from 'react-css-modules';
import List from './List';
import styles from './table.css';
class CustomList extends React.Component {
render () {
let itemTemplate;
# 使用styles属性,从父组件传递下去即可
itemTemplate = (name) => {
return <li className={this.props.styles['item-template']}>{name}</li>;
};
return <List itemTemplate={itemTemplate} />;
}
}
export default CSSModules(CustomList, styles);
方法2: 在父组件内部调用CSSModules, 对子组件进行修饰:
import React, {Component} from 'react';
impot CSSModules from 'react-css-modules';
import List from './List';
import styles from './tabel.css';
class CustomList extends Component {
render() {
let itemTemplate;
itemTemplate = (name) => {
return <li styleName="item-template">{name}</li>;
};
# 内部调用CSSModules
itemTemplate = CSSModules(itemTemplate, this.props.styles);
return <List itemTemplate={itemTemplate} />;
}
}
export default CSSModules(CustomList, styles);
5.CSSModules选项
CSSModules有2种写法:
CSSModules(Component, styles, options)
// 或者
CSSModules(Component, styles)
options :
1.allowMultiple: 默认值为false
是否允许声明多个类, false则表示不允许:
<div styleName='foo bar' /> // 不允许则报错
2.errorWhenNotFount: 默认值为 true,
如果styleName在 css modules中没有找到则会报错
6.使用global css
:global .foo {
// ...
}
这种使用的比较少
7.对于选择性的类名使用styles属性
我们经常会碰到这样的类名情况:
<div className={this.props.showMsg ? 'msg--visble': 'msg--hidden'}>
</div>
使用react-css-modules如何处理这种问题呢?
关键在于被CSSModules装饰的组件继承 styles
属性, 用来映射css modules 和 css classes,即:
class App extends React.Component {
render() {
<div>
<p styleName='foo'></p>
<p className={this.props.styles.foo}></p>
</div>
}
}
在这个例子中,styleName='foo'
和 className={this.props.styles.foo}
是等同的!!!
所以上面问题的解决办法就是:
class App extends Component {
// ...
render() {
# 先声明这个变量
let visible = this.props.showMsg ? 'msg-visible' : 'msg-hidden';
return (
<div
# 然后在这用className来代替styleName
# 注意因为visible含有 '-'等字符,所以使用[]的方式
className={this.props.styles[visible]}
>
...
</div>
)
}
}
总结
- react-css-modules github
- css modules examples
- 学习当className随事件变化时的写法(第7条)
- 主要目的学习以下react中书写样式的一种方式
- webpack中如何配置css相关的loaders