🌑

Mocha's Blog

目录
  1. 几种方案
    1. styled-components
      1. 缺点
    2. Emotion
      1. 缺点
      2. 丰富的 API
    3. JSS
  2. 总结

CssInJS 二三事

发布时间:2024年5月28日

随着单页应用成为前端开发的主流模式,页面之间原有的通过不同 html 资源实现样式隔离的方法不复存在,但随着组件化开发的深入,组件间样式干扰的问题也越来越频繁,前端行业开始寻找新的方法来管理样式,以适应更复杂的项目架构和开发流程。
在这个过程中,先后出现了以下几种解决方案:

  1. 在组件外层添加一个包裹 DIV,使用特殊的 class 或者 id
  2. 在 1 的基础上配合 BEM 规则(使用 -、__、– 区分不同的场景,解决了组件内部元素命名问题)
  3. CssModule、Vue - Scoped
  4. CssInJS

作为最新阶段的产物,CssInJS 解决了前面几个阶段未能完全解决的问题:

  1. 命名冲突:通过局部作用域,防止样式之间的命名冲突。
  2. 依赖管理:像JS模块一样管理样式依赖,确保只加载所需的样式。
  3. 动态样式:容易实现基于组件状态的动态样式。
  4. 可维护性:将样式与组件紧密集成,提高样式的模块化和可重用性。
  5. 服务器端渲染:与JS代码一起更容易实现服务器端渲染。

下面介绍几个不同的 CssInJS 的前端库以及他们的心智模型

几种方案

image.png

styled-components

styled-components 是目前最受欢迎(也是特别早推出)的 CssInJS 库之一,也是是目前 Umi 框架推荐的方案,其允许用户通过模板字符串的方式书写样式,是 CSS 组件化的实现,其设计思路 —— 按照组件的方式写 CSS。

在使用 styled-compoents 作为 CssInJS 方案的项目中,其项目中的页面构成将变成下面这样:

image.png
image.png

通过这样的代码组织,我们可以

  1. 实现 通用 CSS 类型 => 通用 CSS 组件 的心智转换,更符合现在组件化开发的趋势
  2. 通过变量的定义,一个 CSS 组件在最终渲染时可以展现出不同的样式,且各自互不影响

缺点

styled-component 的主要劣势在于它把类名的生成完全放在运行时,如果页面中节点较多,比较容易出现性能问题

Emotion

emotion 类似于 styled-components,但它更加灵活,提供了两种写法:对象样式和字符串模板样式。除此之外,emotion 最大的优势在于在不使用动态样式的场景下,其类名都是在编译时生成的,天然具有高性能特性(当然,成本不会消失只会转移)

缺点

emotion 方案的缺点有:

  1. 不能开箱即用,编译时需要解决语法的 transform 问题,需要对打包工具进行额外配置
  2. API 特性较多,上手成本高

丰富的 API

import { css } from '@emotion/react';
import styled from '@emotion/styled'

// use template css
const CommonText = css`
  color: rgba(0 0 0 / 85%);
  font-size: 14px;

  @media(min-width: 420px) {
    color: #000;
  }

  &:hover {
    opacity: 0.9;
  }
`

// use object style
const CommonText = css({
  color: 'rgba(0 0 0 / 85%)',
  fontSize: 14,

  '@media(min-width: 420px)': {
    color: #000,
  }

  '&:hover': {
    opacity: 0.9,
  }
})

<p css={CommonText}>Text</p>

// use styled
const CommonText = styled.p`
  color: rgba(0 0 0 / 85%);
  font-size: 14px;

  @media(min-width: 420px) {
    color: #fff;
  }
`
<CommonText>Text</CommonText>

// css array
const BoldStyle = css`
  font-weight: 500;
`

<p css={[CommonText, BoldStyle]}>Bold Style</p>

// composition
const BoldText = css`
  ${CommonText};
  ${BoldStyle};
`
<p css={BoldText}>Bold Style</p>

// dynamic props
const DynamicBox = styled.div({
  backgroundSize: 'cover',
  backgroundPosition: 'center',
  backgroundRepeat: 'no-repeact'
}, (props) => {
  return {
    backgroundImage: `url(${props.imageUrl})`
  }
})


<DynamicBox backgroundImage="xxxxxx">动态盒子</DynamicBox>

JSS

JSS 是最早推出的 CssInJS 的方案,但目前已经处于维护阶段,其也提供了两种实现逻辑:

  1. react-jss 库提供的 hook + css object 用法
  2. styled-jss 库提供的 Component 类写法

JSS 最有代表性的是其插件系统,比如 compose、global、nested 等均是通过插件实现的,但也因为配置复杂导致上手成本较高

总结

从整体去看当前的 CssInJS 方案,可以简单的发现:

  1. 复用粒度上,有两种方案:组件 / CSS
  2. 写法上,也有两种方案:CSS / 对象

文档里提到的几种方案都提供了足够的灵活性供我们选择,从社区来看像 emotion / stylex 这类编译时方案正在成为主流的选择
CssInJS 作为一种 CSS 编写范式,除了会面对来自其他方案的竞争外,其存在的意义也一直在受挑战,尽管存在争议,但其确实为前端界带来了新的思考和解决方案。

Powered By Hexo.js Hexo and Minima. Support By Oracle & Docker-Compose.