大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
最近,Emotion 排名第二的维护者 Sam 所在公司弃用了 CSS-in-JS 方案,引起了不小的讨论。这也是我第一次开始重点关注 CSS-in-JS,我甚至在头条开了一个合集重点讨论 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。
所以,在这篇文章中,我将比较和区分 CSS 和 CSS in JS 的优缺点,以帮助开发者决定哪一个更适合个人的项目。
CSS 是最流行的样式表语言之一,用于向以标记语言编写的网页添加样式。 它使开发人员可以更轻松地自定义颜色、布局、字体样式、段落间距、列大小、文本样式等元素。
有 3 种方法可以在网页中包含 CSS:
一般来说,编写 CSS 样式非常简单。 可以使用元素、类或 ID 选择器将样式应用于 HTML 元素。 例如,下面的样式更改了应用程序中所有段落的颜色和字体。
p { font-family: verdana; color: red;}
尽管 CSS 提供了诸多好处,但它并不是一个完美的解决方案,接下来大家一起来看看它的优缺点。
CSS 的优点可以简单总结为以下几个点:
CSS 的缺点主要包括以下几个点:
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 的优缺点。
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>);
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 prop 的每个元素,Emotion 将渲染<EmotionCssPropInternal> 和 <Insertion> 组件。 如果在许多元素上使用 css prop,Emotion 的内部组件会使 React DevTools 变得混乱,如下所示:
因为组件层级的混乱,对开发者调试带来极大的挑战,这也是 CSS-in-JS 一个比较严重的用户体验问题。
如果开发者从未使用过 Web components 或基于组件的框架会有一定的学习成本,这包括:语法、组件化等全新思路。
同时 CSS-in-JS 也无法使用 CSS 缓存,因为没有维护单独的 CSS 文件。
上面说了什么是 CSS-in-JS,CSS-in-JS 方案的优缺点。 其实 CSS-in-JS 解决方案的库也是非常多,比如常见的:
下图展示了不同 CSS-in-JS 库在过去三年的流行度分析:
关于这些库的详细介绍可以继续阅读我发表的另一篇文章《2023 年最受欢迎的 10 大 CSS-in-JS 库!》,这里不再过多展开。
毫无疑问,CSS 和 CSS-in-JS 都是很棒的样式技术,但是在特定情况下也需要有一个最佳实践。如果页面有复杂的 UI ,同时性能关键的应用程序,使用 CSS 是一个不错的选择。 传统 CSS 从 JavaScript 文件中删除了大量代码行,并缩短了初始页面加载时间。
此外,CSS 方案已经经过多年的实战检验,大多数企业相信可以充分利用 CSS 预处理器和 CSS 模块等技术。 此外,纯 CSS 的学习曲线要简单得多,任何新开发人员都可以很快掌握它。
总的来说,假设有一个专注于性能并且不需要设计系统的应用程序。 在这种情况下,可以考虑使用纯 CSS 或任何相关技术,如 CSS module。其他情况则可以考虑下 CSS-in-JS 方案,但是随着程序规模的扩大,开发者将不得不付出大量额外的努力来维护样式系统。
本文主要和大家探讨“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