React中如何编写CSS?

83 阅读2分钟

组件化CSS的概述

  • 在组件化中选择合适的CSS解决方案应该符合以下条件:
    • 可以编写局部css:css具备自己的具备作用域,不会随意污染其他组件内的元素;
    • 可以编写动态的css:可以获取当前组件的一些状态,根据状态的变化生成不同的css样式;
    • 支持所有的css特性:伪类、动画、媒体查询等;
    • 编写起来简洁方便、最好符合一贯的css风格特点;
    • 等等...

React中编写CSS的方式有哪些?各自有什么优缺点?

内联样式(官方推荐)

  1. style 接受一个采用小驼峰命名属性的 JavaScript 对象,,而不是 CSS 字符串;
    • 并且可以引用state中的状态来设置相关的样式;
  2. 内联样式的优点:
    • 内联样式, 样式之间不会有冲突
    • 可以动态获取当前state中的状态
  3. 内联样式的缺点:
    • 写法上都需要使用驼峰标识
    • 某些样式没有提示
    • 大量的样式, 代码混乱
    • 某些样式无法编写(比如伪类/伪元素)
    export class App extends PureComponent {
      constructor() {
        super()

        this.state = {
          titleSize: 30
        }
      }

      addTitleSize() {
        this.setState({ titleSize: this.state.titleSize + 2 })
      }

      render() {
        const { titleSize } = this.state

        return (
          <div>
            <button onClick={e => this.addTitleSize()}>增加titleSize</button>
            <h2 style={{color: "red", fontSize: `${titleSize}px`}}>我是标题</h2>
            <p style={{color: "blue", fontSize: "20px"}}>我是内容, 哈哈哈</p>
          </div>
        )
      }
    }

普通的css

  1. 普通的css我们通常会编写到一个单独的文件,之后再进行引入。
    • 这样的编写方式和普通的网页开发中编写方式是一致的:
    • 如果我们按照普通的网页标准去编写,那么也不会有太大的问题;
  2. 但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响;
    • 但是普通的css都属于全局的css,样式之间会相互影响;
      • 这种编写方式最大的问题是样式之间会相互层叠掉;

css modules

  1. css modules并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的。
    • 如果在其他项目中使用它,那么我们需要自己来进行配置,比如配置webpack.config.js中的modules: true等。
  2. React的脚手架已经内置了css modules的配置:
    • .css/.less/.scss样式文件都需要修改成 .module.css/.module.less/.module.scss 等;
  3. css modules解决了局部作用域的问题。
  4. 但是这种方案也有自己的缺陷:
    • 引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的;
    • 所有的className都必须使用{style.className} 的形式来编写;
    • 不方便动态来修改某些样式,依然需要使用内联样式的方式;

image.png

CSS in JS

CSS-in-JS的模式就是一种将样式(CSS)也写入到JavaScript中的方式,并且可以方便的使用JavaScript的状态;

  • CSS-in-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等等;
  • 虽然CSS预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点;
  • 比较流行的CSS-in-JS的库有哪些呢?
    • styled-components
    • emotion
    • glamorous

styled-components

ES6中模板字符串

另外一种用法:标签模板字符串

  1. 一个普通的JavaScript的函数:
    • 正常情况下,我们都是通过 函数名() 方式来进行调用的,其实函数还有另外一种 调用方式:
    // ES6: 标签模板字符串
    const name = "why"
    const age = 18

    // 1.模板字符串的基本使用
    const str = `my name is ${name}, age is ${age}`
    console.log(str)

    // 2.标签模板字符串的使用
    function foo(...args) {
      console.log(args)
    }

    // foo("why", 18, 1.88)
    foo`my name is ${name}, age is ${age}`

image.png 2. 如果我们在调用的时候插入其他的变量: - 模板字符串被拆分了; - 第一个元素是数组,是被模块字符串拆分的字符串组合; - 后面的元素是一个个模块字符串传入的内容; 3. 在styled component中,就是通过这种方式来解析模块字符串,最终生成我们想要 的样式的

styled的基本使用

  1. styled-components的本质是通过函数的调用,最终创建出一个组件:
    • 这个组件会被自动添加上一个不重复的class;
    • styled-components会给该class添加相关的样式;
  2. 另外,它支持类似于CSS预处理器一样的样式嵌套:
    • 支持直接子代选择器或后代选择器,并且直接编写样式;
    • 可以通过&符号获取当前元素;
    • 直接伪类选择器、伪元素等;

image.png

props、attrs属性

  1. props可以传递
     <SectionWrapper type="password" left="20px">
    </SectionWrapper>
  1. props可以被传递给styled组件

    • 获取props需要通过${}传入一个插值函数,props会作为该函数的参数;
    • 这种方式可以有效的解决动态样式的问题;
  2. 添加attrs属性

    //app
    export class App extends PureComponent {

      constructor() {
        super()

        this.state = {
          size: 30,
          color: "yellow"
        }
      }

      render() {
        const { size } = this.state

        return (
          <AppWrapper>
            <SectionWrapper size={size}>
              <h2 className='title'>我是标题</h2>
              <p className='content'>我是内容, 哈哈哈</p>
              <button onClick={e => this.setState({color: "skyblue"})}>修改颜色</button>
            </SectionWrapper>

            <Home/>

            <div className='footer'>
              <p>免责声明</p>
              <p>版权声明</p>
            </div>
          </AppWrapper>
        )
      }
    }
    
    //子元素单独抽取到一个样式组件
    export const primaryColor = "#ff8822"
    export const secondColor = "#ff7788"

    export const smallSize = "12px"
    export const middleSize = "14px"
    export const largeSize = "18px"
    
    //使用
    // 1.基本使用
    export const AppWrapper = styled.div`
      .footer {
        border: 1px solid orange;
      }
    `

    // const obj = {
    //   name: (props) => props.name || "why"
    // }


    // 2.子元素单独抽取到一个样式组件
    // 3.可以接受外部传入的props
    // 4.可以通过attrs给标签模板字符串中提供的属性
    // 5.从一个单独的文件中引入变量
    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};
      }
    `

styled高级特性

  1. 支持样式的继承
    const MyButton = styled.button`
      border: 1px solid red;
      border-radius: 5px;
    `

    export const MyButtonWrapper = styled(MyButton)`
      background-color: #0f0;
      color: #fff;
    `
  1. styled设置主题
    import { ThemeProvider } from "styled-components"
    
    <ThemeProvider theme={{ color: "purple", size: "50px" }}>
      <App />
      <Profile />
    </ThemeProvider>
    
    const PRofileWrapper =styled.div`
        color: ${props => props.theme.color}
        font-size: ${props => props.theme.fontSize}
    `

添加class

  1. React在JSX给了我们开发者足够多的灵活性,像编写JavaScript代码一样,通过一些逻辑来决定是否添加某些class:

image.png

  1. classnames

用于动态添加classnames的一个库

image.png

    constructor() {
        super()

        this.state = {
          isbbb: true,
          isccc: true
        }
      }

      render() {
        const { isbbb, isccc } = this.state

        const classList = ["aaa"]
        if (isbbb) classList.push("bbb")
        if (isccc) classList.push("ccc")
        const classname = classList.join(" ")

        return (
          <div>
            <h2 className={`aaa ${isbbb ? 'bbb': ''} ${isccc ? 'ccc': ''}`}>哈哈哈</h2>
            <h2 className={classname}>呵呵呵</h2>

            <h2 className={classNames("aaa", { bbb:isbbb, ccc:isccc })}>嘿嘿嘿</h2>
            <h2 className={classNames(["aaa", { bbb: isbbb, ccc: isccc }])}>嘻嘻嘻</h2>
          </div>
        )
      }