React中书写CSS的方式
Vue中书写样式的方法:
- 在单文件组件( .vue)的
<style></style>标签内 书写组件样式 - 通过 scoped 属性决定样式是全局的还是局部的
- 通过 lang 属性来设置less、sass等预处理器
- 通过 module 属性来使用 CSS Module
- 内联样式支持 对象 和 数组 两种书写方式来根据状态来改变样式
- 等等....
相较而言,React官方并没有更给出在React中统一的样式风格:
- 在React生态从普通的CSS,到CSS Module,再到CSS in JS,有很多不同的解决方案
- 但是到目前为止也没有统一的方案;
外部CSS文件和内联样式
普通CSS
通常会将普通的css编写到一个单独的文件,之后再进行引入,这样的编写方式和普通的网页开发中编写方式是一致的:
- 普通的css都属于全局的css,样式之间会相互影响;
- 但是在组件化开发中组件是一个独立的模块,需要样式只在自己内部生效,不会相互影响;
- 动态控制class时需要手动拼接字符串
内联样式
内联样式是官方推荐的一种CSS样式的写法:
- style 属性接收一个对象,对象中的属性名使用小驼峰命名
- 可以使用state中的状态来控制相关的样式
优点:
- 不会产生样式冲突
- 可以根据state动态设置样式
缺点:
- 属性名需要使用小驼峰命名
- 大量的样式会导致代码混乱
- 无法编写伪类/伪元素等
classnames库
在React中如果需要根据state动态修改class,需要手动将所有的class根据条件拼接成字符串,显得比较繁琐
import React, { PureComponent } from 'react'
export class App extends PureComponent {
constructor() {
super()
this.state = {
active: true,
bgColor: true
}
}
render() {
const { active, isccc } = this.state
const classList = ["title"]
isbbb && classList.push("active")
bgColor && classList.push("bgColor")
const classname = classList.join(" ")
return (
<div>
<h2 className={`title ${isbbb ? 'bbb': ''} ${isccc ? 'ccc': ''}`}>哈哈哈</h2>
<h2 className={classname}>呵呵呵</h2>
</div>
)
}
}
export default App
可以使用第三方的 classnames 库,简化动态类名的设置
import React, { PureComponent } from 'react'
import classNames from 'classnames'
export class App extends PureComponent {
constructor() {
super()
this.state = {
active: true,
bgColor: true
}
}
render() {
const { active, bgColor } = this.state
return (
<div>
{/* title active */}
<h2 className={classNames("title", "active")}>我是标题</h2>
{/* title */}
<h2 className={classNames("title", { active: false })}>我是标题</h2>
{/* title active */}
<h2 className={classNames({ title: true }, { active: true })}>我是标题</h2>
<h2 className={classNames({ title: true, active: true })}>我是标题</h2>
{/* title active bgColor */}
<h2 className={classNames("title", { active: active, bgColor: bgColor })}>我是标题</h2>
<h2 className={classNames(["title", { active: active, bgColor: bgColor }])}>我是标题</h2>
</div>
)
}
}
export default App
CSS Modules
CSS Modules 不是官方规范或浏览器中的实现,而是构建步骤中的一个过程(在 Webpack 或 Browserify 的帮助下),它改变了类名和选择器的作用域(有点像命名空间)。目的是为了解决 CSS 中全局作用域 类名冲突的问题 。
create-react-app创建的项目中已经默认开启了 CSS Modules,
- 使用CSS Module编写的样式文件的文件名必须为
xxx.module.css - 在组件中引入样式的格式为
import xxx from './xxx.module.css' - 设置类名时需要使用
xxx.选择器名的形式来设置
.title {
font-size: 32px;
color: red;
}
.content {
font-size: 22px;
color: gray;
}
import React, { PureComponent } from 'react'
import styles from "./App.module.css"
export class App extends PureComponent {
render() {
return (
<div>
<h2 className={styles.title}>标题xxxxxx</h2>
<p className={styles.content}>内容, 哈哈哈哈</p>
</div>
)
}
}
export default App
可以看到元素class属性和设置的并不完全一样,这是因为CSS Module通过算法确保了每一个模块中类名的唯一性。
CSS in JS介绍
在React中有 UI 和 逻辑的结合 JSX,又有 CSS-in-JS,所以React又被称为 all in js
CSS-in-JS 并不是React的一部分,而是由第三方库提供的。
- CSS-in-JS 通过JavaScript为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等
- 虽然CSS预处理器也具备某些能力,但是获取动态状态仍然不好处理
- 所以,目前CSS-in-JS 是React中编写CSS最受欢迎的一种方式
目前比较流行的CSS-in-JS的库有:
- styled-components
- emotion
- glamorous
但是,这种开发方式也受到了很多批评
- Stop using CSS in JavaScript for web development
- hackernoon.com/stop-using-…
Styled-Components库
带标签的模板字符串
带标签的模板是ES6中的模板字符串的一种更高级的形式,它允许你使用函数解析模板字面量。
标签函数:
- 第一个参数是一个将模板字符串拆分得到的字符串数组
- 其余的参数与表达式相关
字符串数组:
- 第一个元素是数组,是模板字符串中 字符串 的组合
- 后面的元素是模板字符串传入一个个变量
const person = "Mike";
const age = 28;
function myTag(strings, personExp, ageExp) {
const str0 = strings[0]; // "That "
const str1 = strings[1]; // " is a "
const str2 = strings[2]; // "."
const ageStr = ageExp > 99 ? "centenarian" : "youngster";
return `${str0}${personExp}${str1}${ageStr}${str2}`;
}
const output = myTag`That ${person} is a ${age}.`;
console.log(output); // That Mike is a youngster.
styled-components的使用
安装npm install styled-components
styled-components的本质是通过函数调用,创建出一个组件
- 这个组件会自动添加一个不重复的class,然后给这个class添加样式
- 支持CSS预处理器一样的样式嵌套
基本使用
export const primaryColor = "#ff8822"
export const largeSize = "18px"
import styled from "styled-components"
// 外部变量
import {
primaryColor,
largeSize
} from "./style/variables"
// 基本使用
export const AppWrapper = styled.div`
background-color: #f0f2f5;
border: 1px solid black;
`
// 可以接受外部传入的props,动态修改样式
// 可以通过attrs对props进行修改或者设置默认值
export const SectionWrapper = styled.div.attrs(props => ({
tColor: props.color || "blue"
}))`
border: 1px solid red;
.title {
font-size: ${props => props.size}px;
color: ${props => props.tColor};
&:hover {
background-color: purple;
}
}
.content {
font-size: ${largeSize}px;
color: ${primaryColor};
}
`
import React, { PureComponent } from 'react'
import { AppWrapper, SectionWrapper } from "./style"
export class App extends PureComponent {
constructor() {
super()
this.state = {
size: 32,
color: "black"
}
}
render() {
const { size, color } = this.state
return (
<AppWrapper>
<SectionWrapper size={size} color={color}>
<h2 className='title'>标题:xxxxxxxx</h2>
<p className='content'>内容:xxxxxxxxx</p>
<button onClick={e => this.setState({color: "skyblue"})}>
修改颜色
</button>
</SectionWrapper>
</AppWrapper>
)
}
}
export default App
可以看到最外层元素的 class 是自动生成的
样式的继承
import styled from "styled-components";
const ButtonWrapper = styled.button`
border: 1px solid red;
border-radius: 5px;
`
export const PrimaryButtonWrapper = styled(ButtonWrapper)`
background-color: #409eff;
color: #fff;
`
设置主题
使用ThemeProvider设置主题,可多次使用
import React from 'react'
import ReactDOM from 'react-dom/client'
import { ThemeProvider } from "styled-components"
import App from './App.jsx'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ThemeProvider theme={{ color: "rgba(0,0,0,.85)", size: "50px" }}>
<App />
</ThemeProvider>
</React.StrictMode>
);
import styled from 'styled-components'
export const AppWrapper = styled.div`
color: #409eff;
`
export const HeaderWrapper = styled(AppWrapper)`
font-size: 30px;
background-color: #f0f2f5;
// 使用theme
color: ${props => props.theme.color};
`