12_React中书写CSS的方式

121 阅读4分钟

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中的状态来控制相关的样式

优点:

  1. 不会产生样式冲突
  2. 可以根据state动态设置样式

缺点:

  1. 属性名需要使用小驼峰命名
  2. 大量的样式会导致代码混乱
  3. 无法编写伪类/伪元素等

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,

  1. 使用CSS Module编写的样式文件的文件名必须为 xxx.module.css
  2. 在组件中引入样式的格式为 import xxx from './xxx.module.css'
  3. 设置类名时需要使用 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

但是,这种开发方式也受到了很多批评

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};
`