告别传统 CSS?一文读懂 CSS in JS 的核心思想 – wiki基地

我无法直接创建文件。不过,我已经为您撰写好了关于 CSS-in-JS 的文章,请在此处查看:

告别传统 CSS?一文读懂 CSS in JS 的核心思想

前言:传统 CSS 的痛点

在构建网页样式的世界里,层叠样式表(CSS)是不可或缺的基石。我们习惯于在 .css 文件中书写样式,然后通过 <link> 标签引入,或者直接在 <style> 标签中定义。这种方式简单直观,对于小型项目而言绰绰有余。

然而,随着 Web 应用变得越来越复杂、组件化开发模式成为主流(例如在 React, Vue, Angular 中),传统 CSS 的一些固有问题逐渐暴露出来,成为了大型项目维护的痛点:

  1. 全局命名空间冲突:CSS 的所有选择器都存在于一个全局作用域中。你为组件 A 定义的样式 .title,可能会不经意地污染到组件 B 的同名 class,导致样式混乱。为了避免这种情况,开发者不得不采用 BEM (Block, Element, Modifier) 这样的长命名约定,但这增加了心智负担和代码冗余。
  2. 依赖管理模糊:当一个项目变得庞大时,你很难确定某个样式规则是否还在被使用。删除一个组件后,它对应的 CSS 代码往往被遗忘在某个角落,成为“死代码”,最终导致 CSS 文件越来越臃肿。
  3. 动态样式笨拙:我们需要根据组件的状态(State)或属性(Props)来动态改变样式。传统方法通常是预定义多个 class,然后用 JavaScript 来回切换,这使得状态和样式的关联变得零散和不直观。
  4. 代码分割与按需加载困难:为了优化首页加载速度,我们希望只加载当前页面必需的 CSS。但传统方式很难做到这一点,往往会将整个站点的 CSS 全部打包在一起。

为了解决这些问题,社区探索出了一种新的思路——CSS-in-JS


什么是 CSS-in-JS?

顾名思义,CSS-in-JS 是一种在 JavaScript 文件中编写 CSS 的技术。它不是一门新的语言,而是一种利用 JavaScript 的能力来构建、管理和应用样式的模式。

通过特定的库(如 styled-components, Emotion 等),我们可以将样式与组件紧密地绑定在一起,让样式也成为组件化开发的一部分。

让我们看一个简单的对比:

传统方式:

Button.css:
“`css
.button {
background-color: #007bff;
color: white;
padding: 10px 15px;
border-radius: 4px;
border: none;
}

.button-primary {
background-color: #28a745;
}
“`

Button.js:
“`jsx
import ‘./Button.css’;

function Button({ isPrimary, children }) {
const className = isPrimary ? ‘button button-primary’ : ‘button’;
return ;
}
“`

CSS-in-JS 方式 (以 styled-components 为例):

Button.js:
“`jsx
import styled from ‘styled-components’;

const StyledButton = styled.button`
background-color: #007bff;
color: white;
padding: 10px 15px;
border-radius: 4px;
border: none;

/ 基于 props 动态改变样式 /
background-color: ${props => props.primary ? ‘#28a745’ : ‘#007bff’};
`;

function Button({ primary, children }) {
// primary prop 会被直接传递给 StyledButton
return {children};
}
“`
通过这个简单的例子,我们可以引出 CSS-in-JS 的几个核心思想。


核心思想一:默认作用域,告别全局污染

这是 CSS-in-JS 最具革命性的特点。

  • 问题:传统 CSS 的全局命名空间。
  • 解决方案:CSS-in-JS 库会自动为每个样式组件生成一个唯一的、随机的 class 名称(例如 .sc-a1b2c3d4-0)。这意味着你为 Button 组件编写的样式,只会作用于 Button 组件,绝不会泄露到其他地方。

``jsx
// 你写的代码
const Title = styled.h1

color: blue;
`;

// 实际渲染到 DOM

My Title

“`

  • 收益
    • 样式隔离:开发者可以放心地在任何组件中使用简单的 div, h1, p 等标签,而不用担心样式冲突。
    • 提升可维护性:修改一个组件的样式时,你非常有信心它不会产生意料之外的副作用。

核心思想二:组件化与代码共存 (Co-location)

  • 问题:传统开发中,一个组件的逻辑(JS)、结构(HTML)和表现(CSS)被分散在不同的文件中。理解或修改一个完整的组件,往往需要在多个文件间来回切换。
  • 解决方案:CSS-in-JS 提倡将与某个组件相关的所有代码(包括样式)都放在同一个文件里。

  • 收益

    • 认知负荷降低:打开一个组件文件,它的所有逻辑、结构和样式都一目了然。
    • 真正的“组件”:当你想删除一个组件时,只需删除那一个文件即可。与之相关的样式代码也会被一并删除,自然而然地实现了“死代码消除”。

核心思想三:利用 JS 的全部能力实现动态样式

  • 问题:传统 CSS 实现动态样式的能力有限,通常需要借助 JS 切换 class
  • 解决方案:因为样式就在 JavaScript 代码中,我们可以使用 JavaScript 的所有能力——变量、函数、逻辑判断——来创建样式。

最典型的应用就是根据组件的 propsstate 来改变样式。

``jsx
const Button = styled.button

padding: ${props => props.size === ‘large’ ? ’15px’ : ’10px’};
background: ${props => props.disabled ? ‘grey’ : ‘blue’};
opacity: ${props => props.hidden ? 0 : 1};
`;

// 使用


``
这种方式比操作
className字符串要优雅和直观得多。此外,实现主题切换(如暗黑模式)也变得异常简单,只需通过ThemeProvider` 注入一个主题对象,所有组件都能访问到主题变量。


核心思想四:关键 CSS 提取与代码优化

  • 问题:传统方式下,浏览器通常需要一次性下载整个应用的所有 CSS,这会阻塞页面渲染。
  • 解决方案:由于 CSS-in-JS 知道哪些组件在当前页面被渲染,因此它可以做到只将当前需要的 CSS 注入到 HTML 的 <style> 标签中。

  • 收益

    • 自动实现关键 CSS:用户访问页面时,只会下载渲染该页面所必需的最小化 CSS,显著提升首次加载速度。
    • 无用的 CSS 自动删除:如果一个组件没有被渲染,它的样式就根本不会出现在最终的产物中。

争议与思考

当然,CSS-in-JS 也并非完美无缺,它也带来了一些新的讨论:

  • 运行时性能开销:一些库需要在运行时解析 JS 中的样式并将其注入 DOM,这会带来微小的性能开销。不过,越来越多的库开始支持“零运行时”(Zero-Runtime),在构建时就将样式提取成静态 CSS 文件,从而消除了这个问题。
  • 学习曲线:对于习惯了传统 CSS 的开发者来说,这是一种新的心智模型,需要时间适应。
  • 增加了 JS 包体积:样式代码被打包进了 JavaScript bundle,可能会增大其体积。

结论

CSS-in-JS 并非要彻底取代 CSS,而是提供了一种在现代组件化框架中更高效、更可控地管理 CSS 的方法论。

它通过作用域隔离代码共存动态化能力自动优化,有力地解决了传统 CSS 在大型应用中的诸多痛点。虽然它有自己的权衡之处,但对于构建复杂、可维护、高性能的现代 Web 应用来说,CSS-in-JS 无疑是一个值得深入了解和采纳的强大工具。当你下次被 CSS 的全局性所困扰时,不妨试试这种新范式。

滚动至顶部