22. React中CSS使用方案
React中CSS使用方案
1. React中CSS的概述
-
1.1. 组件化天下的CSS
-
- 前面说过,整个前端已经是组件化的天下:
- 而css的设计就不是为组件化而生的,所以在目前组件化的框架中都在需要一种合适的CSS解决方案。
-
- 在组件化开发中选择合适的CSS解决方案应该符合以下条件:
- 2.1. 可以编写局部css: css具备自己的作用域,不会随意污染其他组件内的元素;
- 2.2. 可以编写动态的css: 可以获取当前组件的一些状态,根据状态的变化生成不同的css样式;
- 2.3. 支持所有的css特性:伪类、动画、媒体查询等;
- 2.4. 编写起来简洁方便、最好符合一贯的css风格特点;
- 2.5. 等等…
-
-
1.2. React中的CSS
-
- 事实上,css一直是React的痛点,也是被很多开发者吐槽、诟病的一个点;
-
- 在这一点上,Vue做的要好于React:
- 2.1. Vue通过.vue文件中编写
<style></style>标签来编写自己的样式; - 2.2. 通过是否添加scoped属性来决定编写的样式是全局有效还是局部有效;
- 2.3. 通过添加lang属性设置你喜欢的less、sass等预处理器;
- 2.4. 通过内联样式风格的方式来根据最新状态设置和改变css;
- 2.5. 等等…
-
- Vue在CSS上虽然不能称之为完美,但是已经足够简洁、自然、方便了,至少统一的样式风格不会出现多个开发人员、多个项目采用不一样的样式风格。
-
- 相比较而言,React官方并没有给出React中统一的样式风格;
- 4.1. 由此,从普通的css, 到css module, 再到css in js,有几十种不同的解决方案,上百个不同的库;
- 4.2. 大家一致在寻找最好的或者最适合自己的css方案,但是到目前位置也没有统一的方案;
-
2. 内联样式CSS写法
-
- 内联样式是官方推荐的一种样式的写法:
- 1.1. style接受一个采用小驼峰命名属性的Javascript对象,而不是CSS字符串
- 1.2. 并且可以引用state中的状态来设置相关的样式;
-
- 内联样式的优点:
- 2.1. 内联样式,样式之间不会有冲突;
- 2.2. 可以童泰获取当前state中的状态
-
- 内联样式的缺点:
- 3.1. 写法生都需要使用驼峰表示
- 3.2. 某些样式没有提示
- 3.3. 大量的样式,代码混乱
- 3.4. 某些样式无法编写(比如伪类/伪元素)
-
- 所以官方依然是希望内联适合和普通的css来结合编写
3. 普通CSS文件写法
-
3.1. 普通的CSS
-
- 普通的css我们通常会编写到一个单独的文件,之后在进行引入。
-
- 这样的编写方式和普遍的网页开发中编写的方式是一致的:
- 2.1. 如果我们按照普通的网页标准去编写,那么也不会有太大的问题;
- 2.2. 但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响;
- 2.3. 但是普遍的css都是属于全局的css, 样式之间会相互影响
-
- 这种编写方式最大的问题是样式之间相互层叠掉
4. CSS Module写法
-
- CSS Module并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的;
- 1.1. 如果在其他项目中使用它,那么我们需要自己来进行配置,比如配置webpack.config.js中的modules: true的的等
-
- React的脚手架已经内置了css modules的配置:
- 2.1.
.css/.less/.scss等样式文件等都需要修改成.module.css/.module.less/.module.scss等; - 2.2. 之后就可以引用并且进行使用了;
-
- css modules确实解决了局部作用域的问题,也是很多人喜欢在React中使用的一种方案;
-
- 但是这种方案也有自己的缺陷:
- 4.1. 引用的类名,不能使用连接符(.home-title), 在Javascript中是不识别的;
- 4.2. 所有的
className都必须使用{style.className}的形式来编写 - 4.3. 不方便
动态类修改某些样式,依然需要使用内联样式的方式
-
- 如果你觉得上面的缺陷还算OK,那么你在开发中完全可以使用css modules来编写,并且也是在React中很受欢迎的一种方式;
5. CSS in js解决方案
-
5.1. 认识CSS in js
-
- 官方文档也有提到过CSS in js这种方案:
- 1.1. "CSS in js"是一种模式,其中
CSS由Javascript生成而不是在外部文件中定义; - 1.2. 注意此功能并不是React的一部分,而是由第三方库提供;
- 1.3. React对样式如何定义并没有明确态度;
-
- 在传统的前端开发中,我们通常会将结构(HTML)、样式(CSS)、逻辑(Javascript)进行分离。
- 2.1. 但是在前面的学习中,我们就提到过,React的思想中认为逻辑本身和UI是无法分离的,所以才会有了JSX的语法;
- 2.2. 样式呢?样式也属于UI的一部分;
- 2.3. 事实上CSS-in-js的模式就是
一种将样式(CSS)也写入到Javascript中的方式,并且可以方便的使用JavaScript的状态 - 2.4. 所以React有被人称之为
All in JS;
-
- 当然,这种开发的方式也收到了很多的批评:
- 3.1. Stop using CSS in JavaScript for web development.(停止使用CSS在JS里面对于web开发来说)
- 3.2. https://hackernoon.com/stop-using-css-in-javascript-for-web-development-fa32fb873dcc
-
-
5.2. 认识style-components
-
- 批评声音虽然有,但是在我们看来很多优秀的CSS-in-JS的库依然非常强大、方便:
- 1.1. CSS-in-JS通过
Javascript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等等; - 1.2. 虽然CSS预处理器也是具备某些能力,但是获取动态状态依然是一个不好处理的点;
- 1.3. 所以,目前可以说CSS-in-jS是React编写CSS最为受欢迎的一种解决方案;
-
- 目前比较流行的CSS-in-JS库有哪些呢?
- 2.1. style-components
- 2.2. emotion
- 2.3. glamorous
-
- 目前可以说style-components依然是社区最流行的CSS-in-JS库,所以我们以style-components的讲解为主;
-
- 安装style-components:
- 4.1.
npm install style-components
-
-
5.3. ES6标签模板字符串
-
- ES6中增加了模板字符串的语法,这个对于很多人来说都会使用。
-
- 但是模板字符串还有另外一走会给你用法:标签模板字符串(Tagged Template Literals)
-
- 我们一起来看看
一个普通的JavaScript的函数:
- 3.1. 正常情况下,我们都是通过函数名()方式来进行调用的,其实函数还有另外一种调用方式
- 3.2.
函数名()方式,函数名`xx参数`- 代码如下:
// 2. 标签模板字符串的使用function foo(...args) {console.log(args)}// 函数调用// foo('why', 18, 1.88)// 一个函数的后面可以跟上一个模板字符串来对这个函数做调用// 会利用在模板字符串里面插入的动态的数据,去做一个字符串的截取// 会做字符串截取之后, 会变成`my name is `、`, age is 18`、``,作为第一个参数兵器是放到数组里面的// 然后动态插入的数据一次作为第二个参数,第三个参数// 拿到数据之后可以对数据做一个解析的foo`my name is ${name}, age is ${age}`
- 我们一起来看看
-
- 如果我们在调用的时候插入其他的变量
- 4.1. 模板字符串被拆分了
- 4.2. 第一个元素是数组,是被模块字符串拆分的字符串组合;
- 4.3. 后面的元素是一个个模板字符串传入的内容;
-
- 在
styled component中,就是通过这种方式来解析模块字符串,最终生成我们想要
的样式的
- 在
-
-
5.4. styled-components的基本使用
-
styled-components的本质是通过函数的调用,最终创建出一个组件:
- 1.1. 这个组件会被自动缇娜机上一个不重复的class;
- 1.1.
styled-components会给改class添加相关的样式;
-
- 另外,他只是类似于CSS预处理器一样的样式嵌套;
- 2.1. 支持
直接子代选择器或后代选择器,并且直接编写样式; - 2.2. 可以
通过&符号获取当前元素; - 2.3. 直接
伪类选择器、伪元素等; - 2.4. 如下图:

-
-
5.5. props、attrs属性
-
<SectionWrapper size={size} color={color}></SectionWrapper>:
-
- props可以被传递给styled组件
- 2.1. 获取props需要通过${}传入一个插值函数,props会作为该函数的参数;
- 2.2. 这种方式可以有效的解决动态样式的维内托
-
- 添加attrs属性
- 如下图:

- style.js代码如下:
// const SectionWrapper = styled.section.attrs({})import styled from 'styled-components'// styled全局对象,// styled.div表示创建一个div组件// `` 模版字符串,模板字符串跟在div的后面,表示div函数调用// styled.div最终返回一个组件,这个组件渲染出来就是一个div, 把这个组件导出去// 1. 基本使用export const AppWrapper = styled.div`.footer {border: 1px solid orange;}`// 2. 子元素单独抽取到一个样式组件里面// 3. 可以接收外部传入的props/* 因为整个组件都是模版字符串,所以可以使用`${}`动态的插入数据 *//* 直接写props.size是找不到数据的,因为没有props变量对象 *//* font-size: ${props.size}px; *//* 动态插入一个函数,到时候函数会自动调用,外部函数styled.div``调用的时候把props作为参数传入 */// 4. 传入默认值:.attrs({ xx:xx}) 链式调用export const SectionWrapper = styled.div.attrs({tColor: (props => props.color || 'blue') })`/* less/css => 不方便引入js中的数据 */border: 1px solid red;.title {font-size: ${props => props.size}px;color: ${props => props.tColor};&:hover {background-color: purple;}}.content {font-size: 20px;color: green;}`- App.jsx代码如下:
import React, { PureComponent } from 'react'import { AppWrapper, SectionWrapper } from './style.js'export class App extends PureComponent {constructor() {super()this.state = {size: 30,color: 'yellow'}}render() {const { size, color } = this.statereturn (<AppWrapper><SectionWrapper size={size} color={color}><h2 className="title">我是标题</h2><p className="content">我是内容,哈哈哈哈哈</p><button onClick={e => this.setState({color: 'skyblue'})}>修改颜色</button></SectionWrapper><div className="footer"><p>免责声明</p><p>版权声明</p></div></AppWrapper>)}}export default App -
- 补充

- 补充
-
-
5.6. styled高级特性
-
- 支持样式的继承
-
const HYButton = styled.button`padding: 8px 30px;border-radius: 5px; `const HYWarnButton = styled(HYButton)`background-color: red;color: #fff; `
-
- styled设置主题
// 1. 引入ThemeProvider import { ThemeProvider } from 'styled-components';// 2. 基本使用和传参 <ThemeProvider theme={{color: 'purple', fontSize: '50px'}}><App /> </ThemeProvider>// 3. 获取主题数据 .header {color: ${props => props.theme.color};font-size: ${props => props.theme.fontSize}; }
-
