您当前的位置: 首页 > 技术文章 > 前端开发

React编写CSS的方案-CSS In JS

作者: 时间:2022-10-12阅读数:人阅读

CSS In JS 编写方案

认识CSS In JS

官方文档也有提到过CSS In JS这种方案:

“CSS-In-JS” 是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义;

注意此功能并不是 React 的一部分,而是由第三方库提供;

React官方对样式如何定义并没有明确态度;

在传统的前端开发中,我们通常会将结构(HTML)、样式(CSS)、逻辑(JavaScript)进行分离

但是在前面的学习中,我们就提到过,React的思想中认为逻辑本身和UI是无法分离的,所以才会有了JSX的语法。

那么CSS样式呢? 样式也是属于UI的一部分;

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

所以React有被人称之为 All In JS;

当然,这种开发的方式也受到了很多的批评:

Stop using CSS In JavaScript for web development, 有一篇文章说到在web开发中, 停止使用CSS In JS, 文章地址给到大家, 有兴趣可以阅读一下

https://hackernoon.com/stop-using-css-in-javascript-for-web-development-fa32fb873dcc

批评声音虽然有,但是在我看来很多优秀的CSS-In-JS的库依然非常强大、方便:

CSS-In-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等等;

虽然CSS预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点;

所以,目前可以说CSS-In-JS是React编写CSS最为受欢迎的一种解决方案;


CSS In JS的库

目前比较流行的CSS-In-JS的库有哪些呢?

styled-components

emotion

glamorous

目前可以说styled-components依然是社区最流行的CSS-In-JS库,所以我们以styled-components的讲解为主;

安装styled-components: npm i styled-components


标签模板字符串

在正式讲解styled-components库之前, 我先来给大家回顾一下ES6模板字符串的另外一种用法(会的可以直接跳过)

ES6中增加了模板字符串的语法,这个对于很多人来说都会使用, 这里我就不再过多赘述, 但是模板字符串还有另外一种用法:标签模板字符串(Tagged Template Literals)

我们一起来看一个普通的JavaScript的函数:

正常情况下,我们都是通过 函数名() 方式来进行调用的

function foo(...args) {
  console.log(args)
}

// 正常情况下调用函数的方式
foo("Hello World")

但是其实函数还有另外一种调用方式, 就是标签模板字符串:

function foo(...args) {
  console.log(args)
}

// 标签模板字符串调用
foo`Hello World`

如果我们在调用的时候插入其他的变量:

模板字符串被拆分了;

第一个元素是数组,是被模块字符串中变量拆分的字符串集合;

后面的元素是一个个模块字符串传入变量的内容;

function foo(...args) {
  console.log(args)
}

const name = "chenyq"
const age = 18

// 标签模板字符串调用
foo`Hello ${name}, my age is ${age}`

在这里插入图片描述

在styled component中,就是通过这种方式来解析模块字符串,最终生成我们想要的样式的


styled基本使用

styled-components的本质是通过函数的调用,最终创建出一个新的组件:

这个组件会被自动添加上一个不重复的class;

styled-components会给该class添加相关的样式;

  • 例如我们有下面这样一个结构
export class App extends PureComponent {
  render() {
    return (
      <div className='app'>
        <h2 className='title'>我是标题</h2>
        <ul className='content'>
          <li className='item'>我是内容1</li>
          <li className='item'>我是内容2</li>
          <li className='item'>我是内容3</li>
          <li className='item'>我是内容4</li>
          <li className='item'>我是内容5</li>
        </ul>
      </div>
    )
  }
}
  • 在一个单独是style.js文件中, 我们通过styled-components库创建出来一个组件, 代替最外层的div, 创建完成后使用需要导入
  • 这里可以使用VS Code插件vscode-styled-components, 安装后就会有代码提示和高亮显示
// style.js文件

import styled from "styled-components"

// 调用div会返回一个组件, 该组件会会渲染成一个div元素, 并且可以添加样式
export const AppWrapper = styled.div`
  color: skyblue;

  .title {
    font-size: 30px;
  }

  .content {
    font-size: 12px;
  }
`
// 导入创建的组件
import { AppWrapper } from './style'

export class App extends PureComponent {
  render() {
    return (
      <AppWrapper>
        <h2 className='title'>我是标题</h2>
        <ul className='content'>
          <li className='item'>我是内容1</li>
          <li className='item'>我是内容2</li>
          <li className='item'>我是内容3</li>
          <li className='item'>我是内容4</li>
          <li className='item'>我是内容5</li>
        </ul>
      </AppWrapper>
    )
  }
}

在这里插入图片描述

另外,它支持类似于CSS预处理器一样的样式嵌套:

支持直接子代选择器或后代选择器,并且直接编写样式;

可以通过&符号获取当前元素;

直接伪类选择器、伪元素等;

export const AppWrapper = styled.div`
  color: skyblue;

  .title {
    font-size: 30px;
  }

  .content {
    font-size: 12px;
    
    .item:nth-child(1) {
      color: red;
    }
    
    .item {
      border: 1px solid #aaa;

      &:hover {
        color: green;
      }
    }
  }
`

如果我们觉得上面css样式过多或者嵌套层级过深的话, 我们是可以根据自己的需求将子元素单独抽离到一个样式组件中

import styled from "styled-components"

export const AppWrapper = styled.div`
  color: skyblue;

  .title {
    font-size: 30px;
  }
`

// 抽离content
export const ContentWrapper = styled.ul`
  font-size: 12px;
  
  .item:nth-child(1) {
    color: red;
  }
  
  .item {
    border: 1px solid #aaa;

    &:hover {
      color: green;
    }
  }
`
import { AppWrapper, ContentWrapper } from './style'

export class App extends PureComponent {
  render() {
    return (
      <AppWrapper>
        <h2 className='title'>我是标题</h2>
        
        <ContentWrapper>
          <li className='item'>我是内容1</li>
          <li className='item'>我是内容2</li>
          <li className='item'>我是内容3</li>
          <li className='item'>我是内容4</li>
          <li className='item'>我是内容5</li>
        </ContentWrapper>
      </AppWrapper>
    )
  }
}

props和attrs属性

我们知道, React在的less/css方案中, 是不方便引入js中的数据的, 而CSS In JS方案解决了这个问题

我们创建的是一个styled组件, 那么既然是一个组件, 我们就可以通过组件的props属性传递数据的

import { AppWrapper } from './style'

export class App extends PureComponent {
  constructor() {
    super()

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

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

    return (
      // 传递props属性
      <AppWrapper size={size} color={color} >
        <h2 className='title'>我是标题</h2>
        <p className='content'>我是内容哈哈哈</p>
      </AppWrapper>
    )
  }
}

styled组件中获取props需要通过 ${} 传入一个插值函数,props会作为该函数的参数;

export const AppWrapper = styled.div`
  .title {
    /* 插值函数 */
    font-size: ${props => props.size}px;
  }

  .content {
    /* 插值函数 */
    color: ${props => props.color};
  }
`

这种方式可以有效的解决动态样式的问题, 只需要动态修改要传入组件的变量即可;

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

  return (
    // 传递props属性
    <AppWrapper size={size} color={color} >
      <h2 className='title'>我是标题</h2>
      <p className='content'>我是内容哈哈哈</p>

      {/* 动态修改样式 */}
      <button onClick={() => this.setState({ size: size + 2 })}>修改字体大小</button>
    </AppWrapper>
  )
}

可以通过attrs属性给标签模板字符串中提供属性

attrs属性可以接受一个对象, 在对象中设置默认值, 但是设置时需要判断是否有传入样式, 否则传入的样式就不会再生效; 也可以接受一个函数, 该函数会接收props参数, 且该函数需要返回一个对象

需要注意的是, 对象中的属性名不能和props里面的变量名相同, 否则会产生递归, 例如这种写法就是错误color: props => props.color || "red",

// 接收函数的话, 函数需要返回一个对象
export const AppWrapper = styled.div.attrs(props => ({
  // 判断传入的样式是否有值
  tColor: props.color || "red",
  tSize: props.size || 12
}))`
  .title {
    font-size: ${props => props.tSize}px;
  }

  .content {
    color: ${props => props.tColor};
  }
`

styled高级特性

styled设置主题

我们可以在一个单独的js文件中, 设置一系列统一的主题相关的变量, 例如主题颜色, 字体大小等等

export const primaryColor = "#f1f1f1"
export const secondColor = "#ccc"

export const smallSize = "12px"
export const largeSize = "20px"

这样, 在任何要使用的地方直接引入使用即可

import styled from "styled-components";
import * as vers from "./style/variables" 

export const AppWrapper = styled.div`
  .content {
    /* 使用主题颜色和字体 */
    font-size: ${vers.largeSize};
    color: ${vers.primaryColor};
  }
`

支持样式的继承(了解)

const MyButton = styled.button`
  padding: 8px 30px;
  border-radius: 20px;
`

// 会将MyButton中的属性继承过来
const MyButtonEx = styled(MyButton)`
  color: red;
`

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:licqi@yunshuaiweb.com

加载中~
如果您对我们的成果表示认同并且觉得对你有所帮助可以给我们捐赠。您的帮助是对我们最大的支持和动力!
捐赠我们
扫码支持 扫码支持
扫码捐赠,你说多少就多少
2
5
10
20
50
自定义
您当前余额:元
支付宝
微信
余额

打开支付宝扫一扫,即可进行扫码捐赠哦

打开微信扫一扫,即可进行扫码捐赠哦

打开QQ钱包扫一扫,即可进行扫码捐赠哦