百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

2023年CSS-in-JS 和 CSS Modules 谁才是最终赢家?

myzbx 2025-03-12 18:29 33 浏览

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

高级前端进阶

最近,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,包括它的优势、缺点、编译时运行时的不同等等,最终让大家对写下的每一行代码都持有足够的信心。话不多说,直接开始进入正题!

前言

在现代 React 应用程序开发中,有许多组织应用程序样式的方法, 比如: CSS-in-JS ,CSS Modules 等等。 本篇文章将尝试回答 CSS-in-JS 和 CSS Modules 哪个更好?

图片来自作者 Sergey:https://dev.to/

很久以前,当网页主要功能是存储文本文档并且不包括用户交互时,会引入属性来设置内容的样式。 随着时间的推移,Web 变得越来越流行,站点变得越来越大,并且有必要重用样式, 此时 CSS ,即级联样式表应运而生。

随着时间的流逝,Web 变得越来越复杂,开发者也面临着 CSS 带来的诸多问题。比如团队合作面临的:样式重用、样式覆盖、样式一致性等诸多问题。此时,一系列的样式命名约定横空出世,例如: Yandex 的 BEM 或 Atomic CSS,通过这些方式在一定程度上解决了样式的可预测性、同时防止命名重复。

但是,约定是脆弱的,没有强制性而且需要过度分散开发者的注意力,最重要的是很可能因为人为因素被破坏。不过幸运的是,后面出现了一系列的 CSS 样式组织技术,比如:CSS Modules(基于文件名、路径、样式名称的散列来转换样式)、CSS-in-JS(JS 运行时将样式添加到 head) 等等。从而从根本上解决了编写 CSS 的原子性、可重用性和副作用。

1.CSS Modules

1.1 什么是 CSS Modules

CSS Modules 是一个 CSS 文件,默认情况下,所有类名和动画名称都在本地范围内(scoped locally)有效。除非使用:gloabl 语法显示声明为全局样式,比如下面的例子:

// 本地有效
:local(.title) {
  color: red;
}
//全局有效
//凡是类:global声明的class,都不会被编译成哈希字符串。
:global(.title) {
  color: green;
}

CSS Modules 允许开发者在 CSS 文件中编写样式,但程序将它们作为 JavaScript 对象使用以进行额外的处理,同时保证安全性。 CSS Modules 目前非常流行,因为它们自动使类和动画名称唯一,因此开发者无需担心选择器名称冲突。

图片来自作者Gabriel Delight:https://blog.openreplay.com/

从等式上看,CSS Modules = JS 对象中的 CSS。CSS Modules 与 CSS 代码结构大致相同,主要区别是调用方法的差异。以静态站点生成器 SSG 的 Gatsby 为例,后缀为.module.css 的文件将自动当做 CSS Module 处理,这与大多数框架的处理机制非常类似。假如有下面名称为 container.module.css 的文件:

// src/components/container.module.css
.container {
  margin: 3rem auto;
  max-width: 600px;
}

下面是对 container.module.css 文件的引用:

// src/components/container.js
import React from 'react';
import * as containerStyles from './container.module.css';
export default function Container({ children }) {
  return 
{children}
; }

在上面的 Container 组件示例中,导入了一个 CSS 模块并将其声明为名为 containerStyles 的 JavaScript 对象。 然后在带有 containerStyles.container 的 JSX className 属性中引用来自该对象的 CSS 类,它将自动替换为动态 CSS 类名称,比如:
container-module--container--3MbgH,最终渲染到 HTML 中。

1.2 CSS Modules 的优缺点

CSS Module 提供的最明显的好处是消除了对 CSS-in-JS 的依赖,以解决作用域(scoping)和特异性问题。以下总结了几点 CSS Modules 的独特优势:

  • 作用域和特异性:CSS Module 成功解决了传统、旧式 CSS 的作用域问题,由于规则写在 CSS Modules 文件中,因此很少有特异性问题。
  • 有组织的代码:保留单独的 CSS 文件促进了更好的样式代码组织,便于团队统一,特别有利于大型项目支撑。
  • 缓存:CSS Modules 最终构建生成的压缩 CSS 文件可以由浏览器缓存,以改善页面加载时间。
  • CSS 预处理:支持 PostCSS、SASS、Less 等 CSS 预处理器。
  • 零学习曲线:开发者可以使用 CSS Modules 而无需学习任何新内容。本质上,CSS Modules 不是将 CSS 改造成编程语言,只是加入了局部作用域和模块依赖而已。
  • 框架支持:开发者无需添加额外的包来使用 CSS Modules, 所有主要框架和库都提供内置支持。例如:React、Vue、Angular、svelte 、Qwik 、Nextjs 等等

虽然 CSS Modules 提供了以上声明的诸多好处,但它也并不是一个完美的解决方案,可以总结为以下几个点:

  • 非标准 :global 属性:在全局范围内定位选择器时,必须使用 :global 规则,这并非 CSS 规范的一部分,只是被 JavaScript 用来标记全局样式。
  • 没有动态样式:使用 CSS Modules,所有声明都进入单独的 CSS 文件。 因此不支持像 CSS-in-JS 的动态样式,因为不能在 CSS 文件中实现任何 JavaScript 相关的能力。
  • 外部 CSS 文件:不能在组件中忽略 CSS 文件和 CSS 模块的使用。 使用 CSS 模块唯一可行的方法是维护和导入外部 CSS 文件。
  • TypeScript 限制:要将 CSS Modules 与 TypeScript 一起使用,开发者必须在 index.d.ts 文件中添加模块定义或使用 webpack 加载器。

比如下面的例子:

/** index.d.ts **/
declare module "*.module.css";
// CSS 模块文件的 TS 模块
declare module "*.module.scss";
// SCSS 中 CSS 模块文件的 TS 模块

当然也可以充分利用
typescript-plugin-css-modules 插件,它是向 TypeScript 语言服务提供有关包含导入的 CSS Modules 文件的类选择器的信息的插件。 对于 VSCode,需要设置 TypeScript LS 来使用工作区版本。

// 插件添加到tsconfig.json文件
{
  "compilerOptions": {
    "plugins": [{ "name": "typescript-plugin-css-modules" }]
  }
}

2.CSS-in-JS

2.1 什么是 CSS-in-JS

CSS-in-JS 允许开发者通过 JavaScript 为组件编写 CSS 属性。CSS-in-JS 始于 2015 年一个名为 JSS 的 JavaScript 库,该库仍在积极维护中。 开发者必须使用 JavaScript 语法向选择器提供 CSS 属性,然后在页面加载后自动将这些属性应用于它们各自的选择器。

图片来自Diogo Rodrigues《CSS-in-JS and the death of traditional CSS》

当 JavaScript 接管渲染和管理类 React 的前端库时,一个名为 styled-components 的 CSS-in-JS 解决方案应运而生。 另一种越来越流行的 Emotion 库也逐渐赢得开发者的好评。比如下面是使用 styled-components 的例子:

import styled from 'styled-components';
const StyledButton = styled.a`
  padding: 0.75em 1em;
  background-color: ${({ primary }) => (primary ? '#07c' : '#333')};
  color: white;
  &:hover {
    background-color: #111;
  }
`;
export default StyledButton;

当然在 CSS-in-JS 的细分场景又分为编译时和运行时方案,两者在输出样式的时机存在差异。

  • 编译时 CSS-in-JS:编译时 CSS-in-JS 的工作原理是在构建时静态分析代码,将其转换为编译组件,然后在运行时将样式代码移动到文档的头部。典型的代表如:Compiled、Vanilla Extract、Linaria、astroturf、style9 等等。
  • 运行时 CSS-in-JS :当组件渲染时,CSS-in-JS 库必须将样式“序列化”为可以插入到文档中的纯 CSS,从而占用额外的 CPU 周期,最终对应用程序的运行性能产生影响。典型的代表如:styled-components、JSS、Emotion、styled-system、Twin、Fela 等等

不论是编译时,还是运行时 CSS-in-JS 方案,两者都有一些各自的优缺点。

2.2 CSS-in-JS 优缺点

JavaScript 开发人员可能更喜欢使用 CSS-in-JS 来设计样式,而不是通过 CSS 类。 CSS-in-JS 方法解决的最大问题是全局范围。 如果您是 JavaScript 开发人员,它还有一些其他优势,这些优势非常有意义。

  • 没有作用域和特异性问题:由于样式在本地范围内可用,因此它们不易与其他组件的样式发生冲突。 开发者不必花费多余的精力来严格命名样式以避免可能的冲突。同时,由于样式是专门为特定组件编写的,没有预先设置子选择器,因此特异性问题很少见。
  • 动态样式:条件 CSS 是 CSS-in-JS 的另一个亮点,也是灵活性的突出表现,在 React、Vue 等前端项目中也是无处不在,比如:切换按钮样式等等。
  • 较少的 CSS 特异性:CSS-in-JS 可以帮助开发者将 CSS 声明的特殊性保持在最低水位,因为开发者唯一使用它设置样式的是元素本身。
  • 简单的主题:CSS-in-JS 允许开发者用 JavaScript 编写主题。 比如:使用 styled-components ThemeProvider 包装器可以快速对组件的主题进行颜色编码。
  • 无痛维护:考虑到 CSS-in-JS 提供的特性和优势,JavaScript 开发人员可能会发现 CSS-in-JS 比管理数百个 CSS 文件更方便。然而,事实仍然是开发者必须对 JavaScript 和 CSS 都有很好的理解才能有效地管理和维护由 CSS-in-JS 支持的大型项目。

CSS-in-JS 确实很好地解决了作用域等诸多问题, 但开发者仍然面临着更大的挑战。比如:渲染阻塞直接影响用户体验。 除此之外,CSS-in-JS 还需要解决一些其他问题。

  • 延迟渲染:CSS-in-JS 会执行 JavaScript 来解析 JavaScript 组件中的 CSS,然后将这些解析后的样式注入到 DOM 中。 组件越多,浏览器首次绘制页面所花费的时间就越多。
  • 缓存问题:CSS 缓存通常用于改进页面加载时间。 由于使用 CSS-in-JS 时不涉及 CSS 文件,因此缓存是一个大问题。 此外,动态生成的 CSS 类名使缓存问题更加复杂。
  • 不支持 CSS 预处理器:使用常规的组件化 CSS 方法,可以轻松添加对 SASS、Less、PostCSS 等 CSS 预处理器的支持, 而 CSS-in-JS 目前做不到。
  • 凌乱的 DOM:CSS-in-JS 将所有样式定义从 JavaScript 解析为普通 CSS,然后使用样式块将样式注入 DOM 。比如大型项目,可能有超过 100+ 样式块需要首先解析,然后注入。同时,以 Emotion 为例, 对于每个使用 css prop 的元素都将渲染 和 组件。 如果在许多元素上使用 css prop,Emotion 的内部组件会使 React DevTools 变得异常混乱,非常难调试。
  • 三方库体积依赖:开发者虽然可以使用外部库添加 CSS-in-JS 功能,但是在实际的 CSS 解析之前,将包含并运行大量 JavaScript,因为从 JavaScript 到 CSS 样式的解析依赖于三方库,从而带来额外的体积包大小。比如:Emotion 为 7.9 kB,styled-components 为 12.7 kB,而 react + react-dom 的体积是 44.5 kB 。
  • 学习曲线&框架支持:CSS-in-JS 缺少许多原生 CSS 和 SCSS 功能,这对于习惯了 CSS 和 SCSS 的开发者来说,要适配 CSS-in-JS 可能面临一些学习成本,甚至挑战。同时,相比于 CSS Modules 方案,框架的支持并不完善。

以上 CSS-in-JS 面临的诸多问题可能会共同导致产品性能低下、难以维护,其中就包含 UI 和 UX 的不一致。

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

3.1 什么时候用 CSS Modules

如果有一个具有大型 UI 、性能关键的应用程序,那么 CSS Modules 是一个不错的选择。 由于 CSS Module 提供的所有内容最终都是基于传统的、非实验性的基础能力,因此这种方法可以更轻松地监控和修复性能。

同时,CSS Modules 文件很容易从选择的任何 CSS 框架中改进代码,因为所处理的只是 CSS。 如前所述,CSS 的一些基本知识足以让开发者快速完成任务。

目前 CSS 的诸多最新特性也值得开发者持续关注,例如:作用域指令和@scope 伪元素,其旨在解决传统 CSS 的老问题。

...

随着这个新功能的引入,以后开发者可能不再需要 CSS Module 或 CSS-in-JS 来解决作用域问题,大家拭目以待。

注意:当在样式表中使用时,:scope 与 :root 作用相同,因为此时没有明确建立作用域元素的方法。 当从 DOM API(例如: querySelector()、querySelectorAll()、matches() 或 Element.closest() 中使用时,:scope 匹配调用该方法的元素。

3.2 什么时候使用 CSS-in-JS

当在处理性能优先级较低、体积较小的应用程序时,CSS-in-JS 解决方案是理想的选择。 而在处理具有庞大设计系统的、性能关键型的应用程序时可以尽量避免使用 CSS-in-JS。

因为,随着应用程序变得越来越大,使用 CSS-in-JS 很容易将问题变得复杂。 将设计系统转换为 CSS-in-JS 需要做大量工作,在我看来,没有 JavaScript 开发人员愿意处理这个问题。

4.本文总结

本文主要和大家探讨 “2023年CSS-in-JS 和 CSS Modules 谁才是最终赢家?”这个问题。因为篇幅有限,文章并没有过多展开,如果有兴趣,文末的参考资料提供了优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!

参考资料

https://marketplace.visualstudio.com/items?itemName=mrmlnc.vscode-scss

https://dev.to/fyapy/sass-vs-css-modules-vs-css-in-js-vs-compile-time-css-in-js-who-wins-4cl

https://dev.to/alexsergey/css-modules-vs-css-in-js-who-wins-3n25

https://www.gatsbyjs.com/docs/how-to/styling/css-modules/

https://blog.logrocket.com/css-vs-css-in-js/

https://blog.logrocket.com/css-vs-css-in-js/#recommendations-where-use-css-in-js

https://github.com/mrmckeb/typescript-plugin-css-modules#visual-studio-code

https://developer.mozilla.org/en-US/docs/Web/CSS/:scope

https://www.ruanyifeng.com/blog/2016/06/css_modules.html

https://www.diogorodrigues.dev/blog/css-in-js-and-the-death-of-traditional-css

相关推荐

如何设计一个优秀的电子商务产品详情页

加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...

怎么在JS中使用Ajax进行异步请求?

大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...

中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革

前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...

前端监控 SDK 开发分享_前端监控系统 开源

一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...

Ajax 会被 fetch 取代吗?Axios 怎么办?

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

前端面试题《AJAX》_前端面试ajax考点汇总

1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...

Ajax 详细介绍_ajax

1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...

6款可替代dreamweaver的工具_替代powerdesigner的工具

dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...

我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

福斯《死侍》发布新剧照 "小贱贱"韦德被改造前造型曝光

时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...

2021年超详细的java学习路线总结—纯干货分享

本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...

不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!

Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...

2025 年 Python 爬虫四大前沿技术:从异步到 AI

作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...

最贱超级英雄《死侍》来了!_死侍超燃

死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...

停止javascript的ajax请求,取消axios请求,取消reactfetch请求

一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...