2023年CSS与CSS-in-JS的比较:你应该选择哪个?

发表时间: 2023-06-01 05:15

家好,很高兴又见面了,我是"高级前端‬进阶‬",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

高级前端‬进阶

最近,Emotion 排名第二的维护者 Sam 所在公司弃用了 CSS-in-JS 方案,引起了不小的讨论。这也是我第一次开始重点关注 CSS-in-JS,我甚至在头条开了一个合集重点讨论 CSS-in-JS 的方案,下面是已经发表的关于 CSS-in-JS 的文章:

  • 《 2023 年的尽头是编译时 CSS-in-JS 方案么?》
  • 《 CSS vs. CSS-in-JS:2023 年你应该如何选择?》
  • 《 2023 年最受欢迎的 10 大 CSS-in-JS 库!》
  • 《 我们为何选择弃用 css-in-js ? 》

我希望通过系列文章的方式带着大家深入的了解 CSS-in-JS,包括它的优势、缺点、编译时运行时的不同等等,最终让大家对写下的每一行代码都持有足够的信心。话不多说,直接开始进入正题!

前言

在现代 Web 应用程序中有多种组织应用程序样式的方法。 开发人员必须做出的一个选择是使用 CSS 或 CSS-in-JS 来设计应用程序。 尽管纯 CSS 是最常见的选择之一,但像 CSS in JS 这样的技术在开发人员中已经非常流行。 例如,2021 年 JS 年度调查发现,52% 的开发人员使用著名的 CSS-in-JS 库 styled-components。

来自 Nipuni Arunodi 的《CSS vs. CSS-in-JS: What You Should Choose》

所以,在这篇文章中,我将比较和区分 CSS 和 CSS in JS 的优缺点,以帮助开发者决定哪一个更适合个人的项目。

1.CSS

1.1 什么是 CSS

CSS 是最流行的样式表语言之一,用于向以标记语言编写的网页添加样式。 它使开发人员可以更轻松地自定义颜色、布局、字体样式、段落间距、列大小、文本样式等元素。

有 3 种方法可以在网页中包含 CSS:

  • 内联:使用 style 属性。
  • 内部:使用 <style> 标签。
  • 外部:使用 <link> 标签链接外部 CSS 文件。

一般来说,编写 CSS 样式非常简单。 可以使用元素、类或 ID 选择器将样式应用于 HTML 元素。 例如,下面的样式更改了应用程序中所有段落的颜色和字体。

p {  font-family: verdana;  color: red;}

尽管 CSS 提供了诸多好处,但它并不是一个完美的解决方案,接下来大家一起来看看它的优缺点。

1.2 CSS 优点

CSS 的优点可以简单总结为以下几个点:

  • 便于使用:CSS 可以轻松地在整个 Web 应用程序中保持一致的样式。开发人员可以轻松地使用类名同时在多个位置更新样式, 同时确保样式元素在多个网页中保持一致。
  • 预处理器支持:SASS 和 LESS 等预处理器使开发人员能够使用变量并对样式代码进行模块化,从而更容易重用代码并保持项目一致性。
  • 高性能:CSS 允许开发人员用少量代码维护许多网页的样式,减少了需要加载的代码行数和文件数,提高了页面渲染速度。
  • SEO 支持:CSS 可以帮助减少网页中的 HTML 代码,使其更有条理并且更容易在搜索引擎中排名。
  • 设备兼容性:用户可以使用多种设备访问 Web 应用程序,CSS 的响应式设计发挥了重要作用,提高用户体验和站点粘性。

1.3 CSS 缺点

CSS 的缺点主要包括以下几个点:

  • 样式互相覆盖:随着应用程序的扩展,开发人员被迫使用内联样式或 !important 语法来防止样式被覆盖。
  • 多个 CSS 级别:CSS 有不同的版本,从 CSS1 到 CSS3,这在 Web 浏览器和开发人员中造成了混淆。 因此,应该确保在一个项目中只使用一个 CSS 级别。
  • 浏览器支持:Web 开发人员必须通过在多个浏览器中运行应用程序来验证样式兼容性,最终站点一致性、保证用户体验。
  • 命名问题:当应用程序变得复杂时,CSS 类命名问题随之而来。 最终可能会创建冲突的甚至重复的类,这会使维护和调试问题变得困难。 因此,建议遵循像 BEM 这样的标准命名约定,以帮助在应用程序中创建定义良好的 CSS 类

2.CSS-in-JS

2.1 什么是 CSS-in-JS

JS 中的 CSS 也是一个非常简单的概念, 它允许通过 JavaScript 使用 CSS 属性将样式应用到组件。 在 JS 中引入 CSS 的主要目的是解决 CSS 中的样式覆盖问题,可以将 CSS in JS 作为内联样式应用到组件中,也可以使用 styled-components 等第三方库。

比如下面的 inline 模式:

const MyComponent = (props) => (  <p style={{ fontFamily: 'verdana', color: 'red' }}>My Component</p>);

下面使用 styled-components 来定义样式:

// @emotion/react (css prop),使用style对象function ErrorMessage({ children }) {  return (    <div      css={{        color: 'red',        fontWeight: 'bold',      }}    >      {children}    </div>  );}// styled-components or @emotion/styled 使用样式字符串const ErrorMessage = styled.div`  color: red;  font-weight: bold;`;

尽管 CSS-in-JS 解决了样式覆盖问题,但它也带来了一些挑战。接下来一起看看在 JS 中使用 CSS 的优缺点。

2.2 CSS-in-JS 的优点

CSS-in-JS 的优点主要包括以下几个点:

没有命名和覆盖问题

无需考虑命名或覆盖问题,因为使用的样式仅限于当前组件。比如下面的纯 CSS 样式:

   .row {     padding: 0.5rem;     border: 1px solid #ddd;   }

由于多人协作项目,或者样式命名不规范等情况很可能造成样式不经意透出到其他组件、模块、甚至页面,导致样式问题排查困难。而 CSS-in-JS 通过默认使样式在本地范围内,从而有效解决了这个问题。

<div css={{ padding: '0.5rem', border: '1px solid #ddd' }}>...</div>

需要注意的是:CSS 模块也可以提供局部范围的样式。

功能灵活性

由于 CSS-in-JS 本质上是 JavaScript 代码,可以为样式规则使用广泛的逻辑,例如:循环、条件、变量、基于状态的样式等。因此,CSS-in-JS 是需要动态的复杂用户界面的不二选择。

// colors.tsexport const colors = {  primary: '#0d6efd',  border: '#ddd',};function MyComponent({ fontSize }) {  return (    <p      css={{        color: colors.primary,        fontSize,        border: `1px solid ${colors.border}`,      }}    ></p>  );}

比如上面的示例,开发者可以在 CSS-in-JS 样式中同时使用 JavaScript 常量(color)和 React 属性/状态(fontSize)。

在样式中使用 JavaScript 常量的能力在某些情况下减少了重复,因为相同的常量不必同时定义为 CSS 变量和 JavaScript 常量。 使用 props & state 的能力允许开发者创建具有高度可定制样式的组件,而无需使用内联样式。

自动生成浏览器特定的扩展

JS 库中的大多数 CSS 都提供内置功能来生成特定于浏览器的样式扩展,例如 -webkit 和 -moz。常见的 CSS-in-JS 的插件
jss-plugin-vendor-prefixer 就提供了这个功能。

比如下面的例子:

const styles = {  container: {    transform: 'translateX(100px)',  },};

编译后将自动生成下面的代码,如添加-webkit 前缀:

.container-0 {  transform: -webkit-translateX(100px);}

可移植性

开发者可以一次编写样式多处使用,因为组件具有所有的逻辑和样式。 它们开箱即用,使开发者能够使用松散链接的组件构建程序。 此外,开发者可以轻松地从组件中删除样式,因为不会影响其他组件。

const StyledParagraph = styled.div`  font-family: verdana;  color: red;`;const MyComponent = (props) => (  <StyledParagraph {...props}> My Component </StyledParagraph>);

2.3 CSS-in-JS 的缺点

CSS-in-JS 的缺点主要包括以下几点。

运行时开销和异常

CSS-in-JS 增加了运行时开销。 当组件渲染时,CSS-in-JS 库必须将样式“序列化”为可以插入到文档中的纯 CSS。很明显,这会占用额外的 CPU 周期,从而对应用程序的性能产生影响。

//更高效的方法是将样式移到组件外部,以便序列化在模块加载时发生一次,而不是在每次渲染时发生。// 比如:使用来自 @emotion/react 的 css 函数来做到这一点import { jsx, css, Global, ClassNames } from '@emotion/react';const myCss = css({  backgroundColor: 'blue',  width: 100,  height: 100,});function MyComponent() {  return <div css={myCss} />;}

比如:React 核心成员和 React Hooks 的原始设计师 Sebastian Markbåge 在 React 18 工作组中就 CSS-in-JS 展开了丰富的讨论。最终指出:在React并发渲染中,CSS-in-JS 导致在 React 渲染时针对所有 DOM 节点的每一帧重新计算所有 CSS 规则,最终非常慢!

总之,运行时 CSS-in-JS 库通过在组件渲染时插入新的样式规则来工作,这从根本上就不利于性能。同时,使用 CSS-in-JS 还有很多可能出错的地方,尤其是在使用 SSR 和/或组件库时。

额外包体积大小

CSS-in-JS 增加了包大小,每个访问网站的用户都必须下载 CSS-in-JS 库的 JavaScript。 Emotion 压缩和 Gzip (MINIFIED + GZIPPED)后为 7.9 kB,styled-components 为 12.7 kB。

虽然这两个库体积都不是很大,但当所有外部依赖加在一起结果可能发生变化。 比如:react + react-dom 的体积是 44.5 kB 。

CSS-in-JS 导致 React DevTools 混乱

对于使用 css prop 的每个元素,Emotion 将渲染<EmotionCssPropInternal> 和 <Insertion> 组件。 如果在许多元素上使用 css prop,Emotion 的内部组件会使 React DevTools 变得混乱,如下所示:

因为组件层级的混乱,对开发者调试带来极大的挑战,这也是 CSS-in-JS 一个比较严重的用户体验问题。

学习曲线与缓存

如果开发者从未使用过 Web components 或基于组件的框架会有一定的学习成本,这包括:语法、组件化等全新思路。

同时 CSS-in-JS 也无法使用 CSS 缓存,因为没有维护单独的 CSS 文件。

2.4 CSS-in-JS 常见库

上面说了什么是 CSS-in-JS,CSS-in-JS 方案的优缺点。 其实 CSS-in-JS 解决方案的库也是非常多,比如常见的:

  • styled-components
  • JSS
  • Emotion
  • styled-jsx
  • css-modules
  • styled-system
  • vanilla-extract
  • Linaria
  • Twin
  • Fela
  • Radium
  • Styletron 等等。

下图展示了不同 CSS-in-JS 库在过去三年的流行度分析:

关于这些库的详细介绍可以继续阅读我发表的另一篇文章《2023 年最受欢迎的 10 大 CSS-in-JS 库!》,这里不再过多展开。

3.CSS 和 CSS-in-JS 如何选择

毫无疑问,CSS 和 CSS-in-JS 都是很棒的样式技术,但是在特定情况下也需要有一个最佳实践。如果页面有复杂的 UI ,同时性能关键的应用程序,使用 CSS 是一个不错的选择。 传统 CSS 从 JavaScript 文件中删除了大量代码行,并缩短了初始页面加载时间。

此外,CSS 方案已经经过多年的实战检验,大多数企业相信可以充分利用 CSS 预处理器和 CSS 模块等技术。 此外,纯 CSS 的学习曲线要简单得多,任何新开发人员都可以很快掌握它。

总的来说,假设有一个专注于性能并且不需要设计系统的应用程序。 在这种情况下,可以考虑使用纯 CSS 或任何相关技术,如 CSS module。其他情况则可以考虑下 CSS-in-JS 方案,但是随着程序规模的扩大,开发者将不得不付出大量额外的努力来维护样式系统。

4.本文总结

本文主要和大家探讨“CSS vs. CSS-in-JS:2023 年你应该选择什么?”。因为篇幅有限,文章并没有过多展开,如果有兴趣,文末的参考资料提供了优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!

参考资料

https://blog.bitsrc.io/css-vs-css-in-js-what-you-should-choose-in-2023-392a600cb977

https://dev.to/srmagura/why-were-breaking-up-wiht-css-in-js-4g9b

https://cssinjs.org/jss-plugin-vendor-prefixer/

https://byby.dev/css-in-js

https://www.npmjs.com/package/@emotion/react

https://github.com/emotion-js/emotion

https://juejin.cn/post/7165670146017591309