Open zxiaohong opened 6 years ago
在最近的React项目中,遇到了CSS处理的问题:
平时的项目开发中,还会有一些类似的CSS问题:
使用CSS模块化可以很好的解决上述问题。目前主要分为两类:
一类是彻底抛弃 CSS,使用 JS 或 JSON 来写样式。Radium, jsxstyle ,react-style 属于这一类。优点是能给 CSS 提供 JS 同样强大的模块化能力;缺点是不能利用成熟的 CSS 预处理器(或后处理器) Sass/Less/PostCSS, :hover 和 :active 伪类处理起来复杂。 另一类是依旧使用 CSS,但使用 JS 来管理样式依赖,代表是CSS Modules。CSS Modules 能最大化地结合现有 CSS 生态和 JS 模块化能力。发布时依旧编译出单独的 JS 和 CSS。它并不依赖于 React,只要你使用 Webpack,可以在 Vue/Angular/jQuery 中使用。 相关概念
一类是彻底抛弃 CSS,使用 JS 或 JSON 来写样式。Radium, jsxstyle ,react-style 属于这一类。优点是能给 CSS 提供 JS 同样强大的模块化能力;缺点是不能利用成熟的 CSS 预处理器(或后处理器) Sass/Less/PostCSS, :hover 和 :active 伪类处理起来复杂。
另一类是依旧使用 CSS,但使用 JS 来管理样式依赖,代表是CSS Modules。CSS Modules 能最大化地结合现有 CSS 生态和 JS 模块化能力。发布时依旧编译出单独的 JS 和 CSS。它并不依赖于 React,只要你使用 Webpack,可以在 Vue/Angular/jQuery 中使用。
CSS Modules 仅作用于class和id选择器 对其他选择器无效
CSS Modules 通过webpack配置引入项目,不依赖于任何框架,只要使用webpack配置后就可以用于React/Vue/Angular/jQuery 项目中.
module.exports = { entry: __dirname + '/index.js', output: { publicPath: '/', filename: './bundle.js' }, module: { rules: [ ... { test: /\.css$/, use:[ {loader:'style-loader'}, { loader:'css-loader', option:{ modules:true } } ] }, ] } }
上面代码中,关键的是在css-loader的option里配置option:{modules:true},表示打开 CSS Modules 功能。
css-loader
option:{modules:true}
使用这种配置方式,css-loader默认将类名编译为唯一的hash串,但不利于class类名的语义化,如:
/*header.css*/ .root { text-align: center; } .header { background-color: #536587; height: 150px; padding: 20px; color: white; } .title { font-size: 1.5em; }
/*header.js*/ import React from 'react'; import styles from './header.css'; const Header =()=>{ return ( <div className={styles.root}> <header className={styles.header}> <h1 className={styles.title}>Welcome to React</h1> </header> </div> ) } export default Header;
<h1/>将被编译为:<h1 class="_1yHZnBWcll0vdb7BCk5Ufm">Welcome to React</h1>
<h1/>
<h1 class="_1yHZnBWcll0vdb7BCk5Ufm">Welcome to React</h1>
同时 style.css文件将被编译为
._1yHZnBWcll0vdb7BCk5Ufm { font-size: 1.5em; }
因此,需要在配置webpack时多做一点,定制编译生成的哈希类名:
rules:[ { test:/.css$/, use:[ {loader:'style-loader'}, {loader:'css-loader', option:{modules:true,localIdentName:'[path][name]__[local]-[hase:base64:5]'} ] } ]
配置localIdentName之后 上面的类名将生成 如下格式:
:local
localIdentName
:global
.normal { color: green; }
/ 以上与下面等价 / :local(.normal) { color: green; } / 定义全局样式 / :global(.btn) { color: red; } / 定义多个全局样式 / :global { .link { color: green; } .box { color: yellow; } }
- #### Compose 组合Class 对于样式复用,CSS Modules 只提供了唯一的方式来处理:`composes` 组合 ##### 复用内部样式 ```css .userInfoBox { position: relative; padding: 0 24px 0 32px; height: 40px; color: white; line-height: 40px; background: #1f1f1f; transition: background .2s ease 0s; } .docDownloadBox { composes: userInfoBox; margin-right: 10px; border-right: 1px solid #444; }
使用docDownloadBox类的HTML
<a className={styles.docDownloadBox}> 文档&下载 </a>
编译为两个class
/* settings.css */ .primary-color { color: #f40; } /* components/Button.css */ .base { /* 所有通用的样式 */ } .primary { composes: base; composes: primary-color from './settings.css'; /* primary 其它样式 */ }
对于大多数项目,有了 composes 后已经不再需要 Sass/Less/PostCSS。但如果你想用的话,由于 composes 不是标准的 CSS 语法,编译时会报错。就只能使用预处理器自己的语法来做样式复用了。 层叠多个class
对于大多数项目,有了 composes 后已经不再需要 Sass/Less/PostCSS。但如果你想用的话,由于 composes 不是标准的 CSS 语法,编译时会报错。就只能使用预处理器自己的语法来做样式复用了。
composes
配合classnames使用
classnames
npm install --save classnames
/* components/submit-button.js */ import React, { Component } from 'react'; import classNames from 'classnames'; import styles from './submit-button.css'; export default class SubmitButton extends Component { render () { let text = this.props.store.submissionInProgress ? 'Processing...' : 'Submit'; let className = classNames({ [`${styles.base}`]: true, [`${styles.inProgress}`]: this.props.store.submissionInProgress, [`${styles.error}`]: this.props.store.errorOccurred, [`${styles.disabled}`]: this.props.form.valid, }); return <button className={className}>{text}</button>; } };
/* components/submit-button.js */ import React, { Component } from 'react'; import classNames from 'classnames/bind'; import styles from './submit-button.css'; let cx = classNames.bind(styles); export default class SubmitButton extends Component { render () { let text = this.props.store.submissionInProgress ? 'Processing...' : 'Submit'; let className = cx({ base: true, inProgress: this.props.store.submissionInProgress, error: this.props.store.errorOccurred, disabled: this.props.form.valid, }); return <button className={className}>{text}</button>; } };
CSS Moduels 本身没有变量的概念,如果需要使用变量,要借助预处理器/后处理器。
安装 PostCSS 和 postcss-icss-values。
npm install --save postcss-loader postcss-icss-values
把postcss-loader加入webpack.config.js。
{ test: /\.css$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, modules:true, localIdentName:'[name]--[local]--[hash:base64:5]' } }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway ], flexbox: 'no-2009', }), require('postcss-icss-values') ], }, }, ], },
然后,定义变量,如variable.css
variable.css
@value primary #0c77f8; @value green #aaf200;
变量和值之间 写不写冒号都可以,但如果和sass配合使用,不要写分号 一次只定义一个变量
App.css中引入变量
App.css
@value variables: "./variable.css"; @value primary from variables; //或者直接 @value primary from "variables.css" .title { background-color: primary; }
//variable.css @value primary #0c77f8;
//App.js import { primary } from './variable.css'; console.log(primary); // -> #0c77f8
css变量在js中只读
在webpack.conf.js中添加exclude/include配置,定义不同的解析规则即可
webpack.conf.js
exclude/include
// webpack.config.js 局部 { test: /\.css$/, exclude:/common.css/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, modules:true, localIdentName:'[name]--[local]--[hash:base64:5]' } }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway ], flexbox: 'no-2009', }), require('postcss-icss-values') ], }, }, ], }, { test:/\.css$/, include:/common.css/, use:['style-loader','css-loader','postcss-loader'] },
设置为全局规则的css类,不会被模块化处理。
babel-plugin-react-css-modules 可以实现使用styleName属性自动加载CSS模块。只需要把className换成styleName即可获得CSS局部作用域的能力,babel插件来自动进行语法树解析并最终生成className。改动成本极小,不会增加JSX的复杂度,也不会给项目带来额外的负担。 import React from 'react'; import './table.css';
babel-plugin-react-css-modules 可以实现使用styleName属性自动加载CSS模块。只需要把className换成styleName即可获得CSS局部作用域的能力,babel插件来自动进行语法树解析并最终生成className。改动成本极小,不会增加JSX的复杂度,也不会给项目带来额外的负担。
babel-plugin-react-css-modules
import React from 'react'; import './table.css';
export default class Table extends React.Component { render () { return
</div>;
} export default Table;
使用`babel-plugin-react-css-modules`: 1. 不必使用驼峰命名; 2. 不用使用styles Object 3. 可以和全局变量自由组合; `<div className='global-css' styleName='local-module'></div>` 参考文献: 1. [CSS Modules 详解及 React 中实践](https://github.com/camsong/blog/issues/5) 2. [CSS Modules 用法教程 - 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2016/06/css_modules.html) 3. [CSS Modules 入门及 React 中实践 | AlloyTeam](http://www.alloyteam.com/2017/03/getting-started-with-css-modules-and-react-in-practice/) 4. [GitHub - css-modules/css-modules: Documentation about css-modules](https://github.com/css-modules/css-modules) 5. [GitHub - css-modules/postcss-icss-values: Pass arbitrary constants between your module files](https://github.com/css-modules/postcss-icss-values) 6. [GitHub - gajus/babel-plugin-react-css-modules: Transforms styleName to className using compile time CSS module resolution.](https://github.com/gajus/babel-plugin-react-css-modules#how-does-it-work)
CSS Modules 详解及应用
在最近的React项目中,遇到了CSS处理的问题:
平时的项目开发中,还会有一些类似的CSS问题:
使用CSS模块化可以很好的解决上述问题。目前主要分为两类:
CSS Modules是什么
原理
使用CSS Modules 有什么优势
启用方法
CSS Modules 通过webpack配置引入项目,不依赖于任何框架,只要使用webpack配置后就可以用于React/Vue/Angular/jQuery 项目中.
上面代码中,关键的是在
css-loader
的option里配置option:{modules:true}
,表示打开 CSS Modules 功能。使用这种配置方式,css-loader默认将类名编译为唯一的hash串,但不利于class类名的语义化,如:
<h1/>
将被编译为:<h1 class="_1yHZnBWcll0vdb7BCk5Ufm">Welcome to React</h1>
同时 style.css文件将被编译为
因此,需要在配置webpack时多做一点,定制编译生成的哈希类名:
配置localIdentName之后 上面的类名将生成 如下格式:
CSS Modules的使用
局部变量和全局变量
:local
构建时按照localIdentName
做规则处理:global
,全局变量,编译后类名不变/ 以上与下面等价 / :local(.normal) { color: green; } / 定义全局样式 / :global(.btn) { color: red; } / 定义多个全局样式 / :global { .link { color: green; } .box { color: yellow; } }
使用docDownloadBox类的HTML
编译为两个class![composes](https://user-images.githubusercontent.com/18692722/37501095-b464d588-2907-11e8-87bb-3a682fa45582.png)
复用外部样式
配合
classnames
使用npm install --save classnames
直接使用
使用 classnames/bind
定义和使用变量
CSS Moduels 本身没有变量的概念,如果需要使用变量,要借助预处理器/后处理器。
定义变量
安装 PostCSS 和 postcss-icss-values。
把postcss-loader加入webpack.config.js。
然后,定义变量,如
variable.css
变量和值之间 写不写冒号都可以,但如果和sass配合使用,不要写分号 一次只定义一个变量
在其他css文件中使用变量,
App.css
中引入变量css和js共享变量
css变量在js中只读
模块化样式和全局样式共存
在
webpack.conf.js
中添加exclude/include
配置,定义不同的解析规则即可设置为全局规则的css类,不会被模块化处理。
使用babel-plugin-react-css-modules
export default class Table extends React.Component { render () { return
} export default Table;