React 中 CSS in JS 的最佳实践,先收藏了

随着React、Vue等支持组件化的MVVM前端框架越来越流行,在js中直接编写css的技术方案也越来越被大家所接受。

为什么前端开发者们更青睐于这些css-in-js的方案呢?我觉得关键原因有以下几点:

  1. css在设计之初对“组件化”的考虑是不完全的,css直接作用于全局,无法直接作用于某个组件内部的样式。

  2. 在我们的前端组件中有很多“组件样式随着数据变化”的场景,但传统css应对这种场景很无力。

  3. 虽然我们可以通过一些规范来规避问题,但是真正用起来太繁琐了,也不利于跨团队的写作。

比如一个遵循BEM规范的表单组件要写成这个样子:

实在是太繁琐了!如果这是一段业务代码(注意,是业务代码),那团队中的其他人去读这段代码的时候内心一定是比较崩溃的。当然,如果是维护基础组件的话,遵守BEM规范「块(block)、元素(element)、修饰符(modifier)」还是非常重要的。

二、React中编写css的几种方式


2-1、有规范约束的className

使用一些命名规范(比如BEM规范)来约束className,比如下面这种:

// style.css

.form {

background-color: white;

}

.form__input {

color: black;

}

import ‘./stype.css’

const App = props => {

return (

)

}

这种方式比较适合基础组件库的开发,主要原因是:

  1. 使用class开发的组件库,业务方可以很方便地由组件样式的覆盖。

  2. 基础组件库一般由专门的团队开发,命名规范能统一。

  3. 使用最基础的class,能有效降低组件库的大小。

2-2、inline styling

const App = props => {

return (

)

}

这种方式是JSX语法自带的设置style的方法,会渲染出来内联样式,它有一个好处是可以在style中使用一些全局变量(但实际上,less等css预处理语言也是支持的)。另外,如果你只是要调一下组件的margin,这种写法也是代码量最小的写法。

2-3、css-loader(CSS Module)

使用webpack的css-loader可以在打包项目的时候指定该样式的scope,比如我们可以这样打包:

// webpack config

module.exports = {

module: {

loaders: [

{

test: /.css$/,

loader: ‘css-loader?modules&importLoaders=1&localIdentName=[name][local]_[hash:base64:5]’

},

]

},

}

// App.css

.app {

background-color: red;

}

.form-item{

color: red;

}

import styles from ‘./App.css’;

const App = props => {

return (

123
456

)

}

这样.app就会被编译为.App__app___hash这样的格式了。这种方式是借助webpack实现将组件内的css只作用于组件内样式,相比于直接写inline styling也是一个不错的解决方案。

但使用style['form-item']这种形式去className的值(并且我们单独编写css文件时一般也都会使用“-”这个符号),我觉得不少开发者会觉得很尴尬……

另外虽然webpack支持“-”和驼峰互相转换,但是在实际开发中,如果面对一个样式比较多的组件,在css文件中使用“-”然后在js组件中使用驼峰也是有一定的理解成本的。

2-4、css-in-js

顾名思义,css-in-js是在js中直接编写css的技术,也是react官方推荐的编写css的方案,在 https://github.com/MicheleBertoli/css-in-js 这个代码仓库中我们可以看到css-in-js相关的package已经有60多个了。

下面以emotion为例,介绍一下css-in-js的方案:

import { css, jsx } from ‘@emotion/core’

const color = ‘white’

// 下面这种写法是带标签的模板字符串

// 该表达式通常是一个函数,它会在模板字符串处理后被调用,在输出最终结果前

// 我们可以通过该函数来对模板字符串进行操作处理

// 详细链接 —— https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

const App = props => {

return (

className={css`

padding: 32px;

background-color: hotpink;

font-size: 24px;

border-radius: 4px;

`}

This is test.

)

}

在开发业务代码的时候,由于维护人员较多且不固定,且代码规模会逐渐增大,不能保证 css 不会交叉影响,所以我们不能只通过规范来约束,而是要通过 css-in-js 这样的方案来解决 css 交叉影响问题。

三、css-in-js方案比较


我们选取了 https://github.com/MicheleBertoli/css-in-js 仓库中支持功能全面且月下载量较多的几个css-in-js方案进行一下比较(其实它们在使用的时候都差距不大,主要是实现原理以及支持的特性有一些不太一样)

从体积来看:emotion的体积是最小的。

从技术生态环境(以及流行程度):styled-components的star最多,文档相对来讲也是最完善的。

从支持的特性来看:emotion、aphrodite、jss支持的特性是最多的。

所以新人可以尝试接触styled-components,综合来看emotion是一个相当不错的选择。

我们团队其实很早就开始使用React + emotion进行前端开发了。当时选择emotion主要的考虑就是它拥有最全面的功能,以及在当时的css-in-js方案中相对最小的体积。

而且emotion是为数不多的支持source-map的css-in-js框架之一。

四、emotion实现原理简介


4-1、emotion效果

首先让我们来看一下emotion做了什么,这是一个使用了emotion的React组件:

import React from ‘react’;

import { css } from ‘emotion’

const color = ‘white’

function App() {

return (

padding: 32px;

background-color: hotpink;

font-size: 24px;

border-radius: 4px;

&:hover {

color: ${color};

}

`}>

This is emotion test

);

}

export default App;

这是渲染出的html:

React App
This is React.js test

我们可以看到emotion实际上是做了以下三个事情:

  1. 将样式写入模板字符串,并将其作为参数传入css方法。

  2. 根据模板字符串生成class名,并填入组件的class="xxxx"中。

  3. 将生成的class名以及class内容放到<style>标签中,然后放到html文件的head中。

4-2、emotion初始化

首先我们可以看到,在emotion实例化的时候(也就是我们在组件中import { css } from 'emotion'的时候),首先调用了create-emotion包中的createEmotion方法,这个方法的主要作用是初始化emotion的cache(用于生成样式并将生成的样式放入<head>中,后面会有详细介绍),以及初始化一些常用的方法,其中就有我们最常使用的css方法。

import createEmotion from ‘create-emotion’

export const {

injectGlobal,

keyframes,

css,

cache,

//…

} = createEmotion()

let createEmotion = (options: *): Emotion => {

// 生成emotion cache

let cache = createCache(options)

// 用于普通css

let css = (…args) => {

let serialized = serializeStyles(args, cache.registered, undefined)

insertStyles(cache, serialized, false)

return ${cache.key}-${serialized.name}

}

// 用于css animation

let keyframes = (…args) => {

let serialized = serializeStyles(args, cache.registered)

let animation = animation-${serialized.name}

insertWithoutScoping(cache, {

name: serialized.name,

styles: @keyframes ${animation}{${serialized.styles}}

})

return animation

}

// 注册全局变量

let injectGlobal = (…args) => {

let serialized = serializeStyles(args, cache.registered)

insertWithoutScoping(cache, serialized)

}

return {

css,

injectGlobal,

keyframes,

cache,

//…

}

}

4-3、emotion cache

emotion的cache用于缓存已经注册的样式,也就是已经放入head中的样式。在生成cache的时候,使用一款名为Stylis的CSS预编译器对我们传入的序列化的样式进行编译,同时它还生成了插入样式方法(insert)。

let createCache = (options?: Options): EmotionCache => {

if (options === undefined) options = {}

let key = options.key || ‘css’

let stylisOptions

if (options.prefix !== undefined) {

stylisOptions = {

prefix: options.prefix

}

}

let stylis = new Stylis(stylisOptions)

let inserted = {}

let container: HTMLElement

if (isBrowser) {

container = options.container || document.head

}

let insert: (

selector: string,

serialized: SerializedStyles,

sheet: StyleSheet,

shouldCache: boolean

) => string | void

if (isBrowser) {

stylis.use(options.stylisPlugins)(ruleSheet)

insert = (

selector: string,

serialized: SerializedStyles,

sheet: StyleSheet,

shouldCache: boolean

): void => {

let name = serialized.name

Sheet.current = sheet

stylis(selector, serialized.styles)  // 该方法会在对应的selector中添加对应的styles

if (shouldCache) {

cache.inserted[name] = true

}

}

}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

本人分享一下这次字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是附赠给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等

详细大厂面试题答案、学习笔记、学习视频等资料领取,点击资料领取直通车免费领取!

前端视频资料:
减轻大家的负担。**

[外链图片转存中…(img-iTZLwNQ6-1712200178757)]

[外链图片转存中…(img-srSwAdPo-1712200178757)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-z5kdOAqj-1712200178758)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

本人分享一下这次字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是附赠给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等

详细大厂面试题答案、学习笔记、学习视频等资料领取,点击资料领取直通车免费领取!

[外链图片转存中…(img-zS1JWgx8-1712200178758)]

前端视频资料:

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值