React中的css到底该怎么写?这一直是个饱受争论的话题。本文将结合React CSS的发展史,分别叙述CSS in js与CSS module两种风格中的最佳实践。css
首先,facebook提出CSS in js
的概念,这很奇怪。 咱们多年来所学的知识都在宣扬关注点分离的重要性,不该该将标记和css混在一块儿。可是React行内样式的提出,试图改变关注点分离这一律念。使其从技术分离向组件分离转变。html
经过CSS in js
,有如下两个优势:webpack
好比这样(先忽略直接将style对象传入带来的性能问题)web
render(){ return ( <div style={{fontWeight: this.props.weight ? 'bold' : 'normal'}}>hello world</div> ) } 复制代码
行内样式也有缺点:设计模式
!important
来实现了事实证实,虽然行内样式解决了目标问题,却引起了更多的问题。bash
若是你认为行内样式的方案不适合本身的团队,但仍然但愿尽可能紧密结合样式与组件,那么webpack
的css-loader
就帮了你大忙! 关于webpack
和详细配置,本文再也不赘述。咱们看一下本文主要用到的loader
和plugin
:markdown
const HTMLWebpackPlugin = require('html-webpack-plugin') module.exports = { ... module: { rules: [ ... { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', options: { modules: true } } ] } ] }, plugins: [ new HTMLWebpackPlugin() ] } 复制代码
如上配置完之后,在终端启动项目后,就能够在网页中访问了。接下来咱们开始编写具体代码。antd
首先定义一个普通的css文件架构
.button { background-color: blue; color: #fff; padding: 10px 20px; } 复制代码
而后在js文件中引入函数
import styles from './index.css' 复制代码
import语句会导入一个样式对象,其全部属性就是index.css中定义的类,这时候运行一下console,开发者工具会输出一个形如这样的对象。这个key是根据文件散列值和其余一些参数生成的。
{ button: "_2wpxM3yizfwbWee6k0U1D4" } 复制代码
接着,咱们能够用这个对象去设置按钮的className属性
const Button = () => ( <button className={styles.button}>Click it</button> ) 复制代码
这样,按钮的样式就设置完成了。 咱们打开开发者工具能够看到,button的类名就是上面的_2wpxM3yizfwbWee6k0U1D4
。若是查看页面头部,咱们还会发现相同的类名已经注入到页面中了。
css-loader
容许你在js模块中导入css文件,而且启用modules
标记时,全部类名都只做用于导入它们的模块。最后,style-loader
接收css-loader
转换的结果,并将样式注入到页面头部。
这种用法很是强大,由于咱们拥有了css的完整能力及表现性,又结合了局部做用域类名与显示依赖的优势。咱们还能够像这样配置localIdentName
参数,解决调试时信息不清晰的问题。
module: { rules: [ { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' } } ] } ] } 复制代码
生产环境不须要这样的类名,更注重性能,所以咱们想要更简短的类名和散列值。 咱们能够在生产环境下使用MiniCssExtractPlugin
将样式提取到单独的css文件,并将其放入CDN
,从而得到更好的性能。
css-loader还支持一些关键词。
第一个就是global
关键词。给任何类添加:global
前缀,意味着请求CSS模块不要为当前选择器加上局部做用域。
例如:
:global .button {
...
}
复制代码
这样,你能够应用不须要局部做用域的样式。好比第三方组件。
第二个就是composes
,有了它,就能够从同个文件或外部依赖中引用类名,将其它类的全部样式应用于一个元素。
例如:
.text-red{ color: red; } .button { composes: text-red; background-color: blue; color: #fff; padding: 10px 20px; } 复制代码
最终,button类的全部规则以及composes声明的全部规则都能做用于元素。
这个特性很是强大,并且原理很巧妙。你可能觉得它和SASS
的@extend
方法同样,只是将组合类复制到引用它们的位置,其实不是这样。简单来说,全部组合类名都是逐个应用到DOM中的组件上。
以咱们的示例来讲,代码以下:
<button class="_2wpxM3yizfwbWee6k0U1D4 Sf8w9cFdQXdRV_i9dgcOq">Click it</button>
复制代码
注入页面的css以下所示:
.Sf8w9cFdQXdRV_i9dgcOq { color: red; } ._2wpxM3yizfwbWee6k0U1D4 { background-color: blue; color: #fff; padding: 10px 20px; } 复制代码
原子级CSS又称函数式CSS,是CSS的一种使用方式,即每一个类只有一条规则。
例如,能够用一个类来设置底部外边距为0:
.mb0 { margin-bottom: 0; } 复制代码
而后用另外一个类设置font-weight为bold
.fwb { font-weight: bold; } 复制代码
而后将这些原子类用在元素上:
<span class="mb0 fwb">Hello World</span>
复制代码
这种技巧存在争议,但很高效。一方面,类是在CSS文件中定义,却在视图层组合,每次修改元素的样式都要同时修改两个地方;另外一方面,它能够超快地搭建页面。
其实,只要全部基本规则都定好,将这些类应用于元素或者用它们生成新的样式都很是快,这是一大优势。其次,使用原子级CSS能够控制css文件的大小,由于建立新组建时能够复用已有类的样式,不须要编写新样式,这对性能颇有好处。
咱们能够将CSS Module与原子级CSS结合起来使用。
查看如下示例:
.title { composes: mb0 fwb; } <span class="title">Hello World</span> 复制代码
这种作法很是好,由于样式逻辑仍然保留在CSS中,同时CSS模块利用composes将全部单个类聚合到一个类中。
上述代码的渲染结果以下所示:
<span class="title--3JCJR mb0--21SyP fwb--1JRhZ">Hello World</span>
复制代码
此处的title
、mb0
以及fwb
都是自动加到元素上的。而且它们都只做用于局部。这样,咱们就用上了CSS Module的全部优点。
最后,让咱们看一下CSS in js中的王者——styled-components。 这个库能够说考虑到了其余组件样式库遇到的全部问题。
在安装styled-components以后,咱们能够这样使用它:
import styled from 'styled-components' const Button = styled.button` background-color: blue; color: #fff; padding: 10px 20px; ` 复制代码
能够看到它使用了模板字符串,这意味着它能够用js的所有能力为元素添加样式。 这种看似奇怪的语法会返回普通的React组件Button,它渲染了一个按钮元素,并加上了模板中定义的样式。先建立惟一的类名,再将它加到元素上,最后向页面文档头部注入相应的样式。至此,样式生效了。
渲染的组件以下所示:
<button class="kYvFOg">Click it</button>
复制代码
页面上添加的样式以下:
.kYvFOg { background-color: blue; color: #fff; padding: 10px 20px; } 复制代码
这个库的优势以下在于支持几乎全部的css特性。好比,它支持SASS
风格的伪类语法:
const Button = styled.button` background-color: blue; color: #fff; padding: 10px 20px; &:hover { background-color: #fff; color: blue; } ` 复制代码
它也支持媒体查询:
const Button = styled.button` background-color: blue; color: #fff; padding: 10px 20px; &:hover { background-color: #fff; color: blue; } @media (max-width: 480px) { padding: 10px 10px; } ` 复制代码
它还能够很方便地覆盖样式,并设置不一样属性来屡次复用该组件。当你使用了形如antd
之类的UI库,并但愿自定义一些样式时,这个优势会很是明显。
CSS in or not in js ? this is a question.
我以为没有绝对的最佳实践,只有在工程迭代的过程当中,不停的找寻最适合工程、最适合团队的方案,这才是最明智的选择。