为什么我们正在放弃 CSS-in-JS

这篇文章将深入的挖掘我当时为什么会在项目中使用 css-in-JS (本文使用 Emotion 方案 ),而现在为什么正在放弃这样的方案 。
什么是 CSS-in-JSCSS-in-JS 允许你直接使用 JAVAScript 或者 TypeScript 修改你的 React 组件的样式
import styled from '@emotion/styled'const ErrorMessageRed = styled.div`color: red;font-weight: bold;`;function App() {return (<div><ErrorMessageRed>hello ErrorMessageRed !!</ErrorMessageRed></div>);}export default App;styled-components 和 Emotion 是 React 社区最流行的 CSS-in-JS 方案 。本文中我只是提及到 Emotion,但是我相信大部分的使用场景也同样适用于 styled-components 。
本文专注于 运行时类型的 CSS-in-JS ,styled-components 和 Emotion 都属于这个类型 。因为 CSS-in-JS 还有另一种类型,编译时类型 CSS-in-JS 这块会在文章末段稍微提及到 。
CSS-in-JS 的优缺点在我们深入了解 CSS-in-JS 的模式和它对性能的影响之前,我们先从总体的了解一下为什么我们会使用这项技术以及为什么要逐步放弃
优点1.Locally-scoped styles: 当我们在裸写 CSS 的时候,很容易就污染到其他我们意想不到的组件 。比如我们写了一个列表,每一行的需要加一个内边距和边框的样式 。我们可能会写这样的 CSS 代码
.row {padding: 0.5rem;border: 1px solid #ddd;}几个月之后可能你已经忘记了这个列表的代码了,然后你写了 className="row" 在另外的组件上,那么这个新的组件有了内边距合边框样式,你甚至都不知道为什么会这样 。你可以使用更长的类名或者更加明确的选择器来避免这样的情况发生,但是你还是无法完全保证不会再出现这样的样式冲突 。
CSS-in-JS 就可以通过 Locally-scoped styles 来完全解决这个问题 。如果你的列表代码这么写的话:
<div className={css`padding: 0.5rem;border: 1px solid #ddd;`}> ...row item... </div>这样的话,内边距和边框的样式永远不会影响到其他组件 。

提示:CSS Modules 也提供了 Locally-scoped styles
2. Colocation: 你的 React 组件是写在 src/components 目录中的,当你裸写 CSS 的时候,你的 .css 文件可能是放置在 src/styles 目录中 。随着项目越来越大,你很难明确哪些 CSS 样式是用在哪些组件上,这样最后你会冗余很多样式代码 。
一个更好的组织代码的方式可能是将相关的代码文件放在同个地方 。这种做法成为「共置」,可以通过这篇文章了解一下 。
问题在于其实很难实现所谓的「共置」 。如果在项目中裸写 CSS 的话,你的样式和可能会作用于全局不管你的 .css 文件被放置在哪里 。另一方面,如果你使用 CSS-in-JS,你可以直接在 React 组件内部书写样式,如果组织得好,那么你的项目的可维护性将大大提升 。
提示:CSS Modules 也提供了「共置」的能力
3. 在样式中使用 JavaScript 变量: CSS-in-JS 提供了让你在样式中访问 JavaScript 变量的能力
function App(props) {const color = "red";const ErrorMessageRed = styled.div`color: ${props.color || color};font-weight: bold;`;return (<div><ErrorMessageRed>hello ErrorMessageRed !!</ErrorMessageRed></div>);}上面的例子展示了,我们可以在 CSS-in-JS 方案中使用 JavaScript 的 const 变量 或者是 React 组件的 props 。这样可以减少很多重复代码,当我们需要同时在 JavaScript 和 CSS 两侧定义相同的变量的时候 。我们通过这样的能力可以不需要使用 inline styles 这样的方式来完成高度自定义的样式 。( inline styles 对性能不是特别友好,当我们有很多相同的样式写在不同的组件的时候)
中立点1. 这是热门的新技术: 许多的开发者包括我自己,会更热衷于使用 JavaScript 社区中热门的新技术 。一个重要的原因是,很多新的框架或者库,能够提升带来巨大的性能或者体验上的提升(想象一下,React 对比 jQuery 带来的开发效率提升) 。另一个原因就是,我们对新技术抱有比较开放的态度,我们不愿意错过每个大事件 。当然了,我们在选择新的技术的时候也会考虑到它带来的负面影响 。这大概就是我之前选择 CSS-in-JS 的原因 。
缺点
  1. CSS-in-JS 的运行时问题 。当你的组件进行渲染的时候,CSS-in-JS 库会在运行时将你的样式代码 ”序列化” 为可以插入文档的 CSS。这无疑会消耗浏览器更多的 CPU 性能
  2. CSS-in-JS 让你的包体积更大了 。 这是一个明显的问题 。每个访问你的站点的用户都不得不加载关于 CSS-in-JS 的 JavaScript 。Emotion 的包体积压缩之后是 7.9k,而 styled-components 则是 12.7 kB。虽然这些包都不算是特别大,但是如果再加上 react & react-dom 的话,那也是不小的开销 。


    推荐阅读