关于css-in-js

前言

我们在日常开发中,经常会面临是否使用 css-in-js 的问题,比如组件库的封装,主题定制等需求。所以 css-in-js 一直是前端领域中一个热门话题。本文主要讨论一些实用场景以及 css-in-js 的优缺点。

CSS 的渲染阻塞

渲染阻塞是最常见的前端性能问题,传统上浏览器先加载 html,然后从所有外部资源加载 css。之后在使用所有外部和内部 css 创建 cssom。最后根据级联规则为 html 提供样式。这个过程会导致 css 阻塞页面的呈现,延迟首屏绘制,产生很多负面影响。

如何解决 css 渲染阻塞

  • 使用 http/2,对多个 html/css/js 文件可以并行加载,可以最大限度减少由于等待其他文件加载而导致的渲染阻塞。

  • 一个页面可能有很多 css,其中也包括不使用的选择器,导致最终的 css 样式中有很多的垃圾。css 越多,构建 cssom 所需要的时间越长,最终会导致不必要的渲染阻塞。 所以将 css 分成小块是非常有用的: 我们可以将全局样式和关键 css 保存在一个通用的 css 文件中,然后将其他内容组件化。传统的做法是每个组件编写对应的 css 文件,css 会随该组件导入时而导入,移除时而移除。 但这种做法有很明显的缺点:不同的组件对应的 css 中可能存在重复的类名,而这些 css 文件的作用域是全局的,样式覆盖在所难免。所以在类名的命名上我们应该遵循严格的规范。

css-in-js 做了什么?

css-in-js,顾名思义,就是允许用 js 为组件编写 css 属性,起源于名为 jss 的 js 库,类似的库还有 styled-components、vanilla-extract 、emotion 等,更多 css-in-js 解决方案,可点击查阅此处, 在之前的项目里,团队基于 antd 做组件库二次封装的时候就使用到了 styled-components,简单介绍下使用方法。

创建一个带样式 button 组件(主要基于标签模板书写):

cssInJs

在需要使用的地方导入即可:

cssInJs

值得注意的是,应用于有样式组件的样式是局部作用域的,这消除了需要注意 CSS 类命名和全局作用域的麻烦。此外,我们可以根据提供给组件的 props 或应用程序功能所需的任何其他逻辑动态添加或删除 CSS。

扩展 vanilla-extract :

也是一款目前比较火热的 css-in-js 的解决方案,跟 styled-components 不同的是,vanilla 类型安全, 高度兼容 TS,称之为 css-in-ts 更为贴切。在之前的项目里也有涉猎过。这里简单介绍下它的特点:

  • 零运行时:不同于其他 css-in-js 库的解决方案:在运行时向文档 head 中注入样式。vanilla 会在编译时,输出 css modules 和 css 内容,不需要带任何运行时内容到生产环境,产物体积更小,更符合提升 css 性能(单文件)的做法。
  • 支持 ts 类型检查,css 安全。
  • 不受框架限制

使用方法

style(): 创建一个自动范围的 CSS 类。传入元素的样式,然后导出返回值。在你的用户代码中的某个地方导入这个值,它就会被转换为一个范围内的类名。

cssInJs

注意:样式函数中的属性只能影响一个单一的 HTML 节点。这意味着你不能使用嵌套来声明一个元素的子元素的样式--这是你在 Sass 或 PostCSS 中可能习惯的。相反,你需要为子元素单独设置样式。如果一个子元素需要基于父元素的不同样式,你可以使用 selectors 属性来添加依赖于父元素的样式。

cssInJs

createTheme:常见于主题定制

cssInJs

css-in-js 的优点

  1. 没有范围和优先级问题:由于样式在局部范围内可用,因此它们不容易与其他组件的样式冲突。你甚至不必担心严格地命名事物以避免样式冲突。 样式是专门为一个组件编写的,没有预先设置子选择器,因此很少出现优先级问题。

  2. 动态样式:条件 CSS 是 CSS-in-JS 的另一个亮点。正如上面的button所演示的,检查 prop 值并添加合适的样式。

  3. 主题定制(使用css自定义属性也是非常方便的):CSS-in-JS 允许你完全用 JavaScript 编写主题逻辑。使用 styled-components ThemeProvider 包装器,可以快速对组件的主题进行颜色编码。

  4. 易于维护:比管理多个css文件方便的多

css-in-js 的缺点

  1. 延迟渲染:CSS-in-JS 将执行 JavaScript 从 JavaScript 组件解析 CSS,然后将这些解析后的样式注入到 DOM 中。组件越多,浏览器第一次绘制所花费的时间就越多。而这正击中了上文中的提到的阻塞渲染的痛点。

  2. 缓存问题:CSS 缓存通常用于提高连续页面加载时间。由于使用 CSS-in-JS 时不涉及 CSS 文件,缓存是一个大问题。此外,动态生成的 CSS 类名使这个问题更加复杂。

  3. 不支持css预处理器:无法支持Sass、Less、PostCSS 等预处理器。

  4. 混乱的dom结构:CSS-in-JS 基于这样的思想:将所有的样式定义从 JavaScript 解析为普通的 CSS,然后使用样式块将这些样式注入到 DOM 中。所以只要组件使用了css-in-js,就会有样式块解析并注入到dom。组件越多,注入的样式就越多。

  5. 没有广泛的支持:大多数UI和组件库都不支持。


Built with Next.js • Deployed on Vercel
©2024 Owen