一文详解 CSS-in-JS

296次阅读  |  发布于3年以前

几年前,如果有人提到用 JavaScript 编写 HTML 作为构建大型网站的一种方式,很多开发者会当这作不可理喻的想法,但是现在,使用 React、Vue 和 Angular 框架为组件开发的应用正在慢慢替代传统的 Web 开发。 现在 CSS-in-JS 确实也有点像当年的味道,虽然并不是唯一的解决方案,却提供了一个很大胆的想法和尝试。 对现代化的 Web 开发项目说,CSS 也是如此,CSS 做为 Web 的样式表来呈现丰富多彩的 Web 应用已经不再是唯一的选择了,我们或许应该多考虑其他的扩展性和移植性尝试未来的 CSS-in-JS。

一 CSS 的介绍 CSS(层叠样式表)是一种用来为结构化文档添加样式的计算机语言,由 W3C 定义和维护。目前最新版本是 CSS2.1,为 W3C 的推荐标准。CSS3 现在已被大部分现代浏览器支持,而下一版的 CSS4 仍在开发中。 1 模块和标准化进程 CSS Level 2 经历了 9 年的时间(从 2002 年 8 月到 2011 年 6 月)才达到 Recommendation(推荐) 状态,主要原因是被一些次要特性拖了后腿。为了加快那些已经确认没有问题的特性的标准化速度,W3C 的 CSS Working Group(CSS 工作组) 作出了一项被称为 Beijing doctrine 的决定,将 CSS 划分为许多小组件,称之为_模块_。这些模块彼此独立,按照各自的进度来进行标准化。其中一些已经是 W3C Recommendation 状态,也有一些仍是 early Working Drafts(早期工作草案)。当新的需求被肯定后, 新的模块也会同样地添加进来。 第一个 CSS 于1996年推出,下面是 CSS 版本的时间表:

2 CSS 模块状态 从形式上来说,CSS3 标准自身已经不存在了。每个模块都被独立的标准化,现在标准 CSS 包括了修订后的 CSS2.1 以及完整模块对它的扩充,模块的 level(级别)数并不一致。可以在每个时间点上为 CSS 标准定义一个 snapshots(快照),列出 CSS 2.1 和成熟的模块。 W3C 会定期的发布这些 snapshots,如 2007, 2010, 2015 或 2017。 目前为止,还没有 level 超过 3 的模块被标准化,未来应该会有所改变。不过有些模块,比如 Selectors(选择器)4 或 CSS Borders and Backgrounds(边框和背景)Level 4 早已拥有了 Editor's Draft(编辑草案),即使它们还没达到 First Published Working Draft(初次发布工作草案)状态。

3 五种 CSS 设计模式 现代化的前端开发在历史上发展了许多的 CSS 设计模式,主要发展出以下几种:

其设计的原因基本是基于这几个问题来做优化的:

4 Atomic CSS 的历史

Tailwind CSS 和其他预编译器相比还是比较的冷门,如下图:

在 React 和 Vue 日益吞噬的 Web 开发界中,组件化的思想和工程化日渐成熟,Atomic CSS 也算是比较早推出的一个设计思想,笔者觉得 Atomic CSS 能做的事情,在 CSS-in-JS 反而能做的更好,因为 JS 框架和工具的盛行和丰富,Atomic(原子化)也是未来 CSS-in-JS 一个可以涉足的区域。

5 CSS 数学表达式 根据 CSSWG 的 draft,CSS 目前支持计算的数学表达式主要包含五大类:

日常使用中 calc() 算是最常用的,一般用来计算长宽、响应式布局等等,而比较函数在一些场景也可能会用的上,剩下的其他函数很大部分都没有机会在项目中使用的上。

6 CSS Houdini

Houdini是一组底层API,它们公开了CSS引擎的各个部分,从而使开发人员能够通过加入浏览器渲染引擎的样式和布局过程来扩展CSS。Houdini是一组API,它们使开发人员可以直接访问CSS 对象模型 (CSSOM),使开发人员可以编写浏览器可以解析为CSS的代码,从而创建新的CSS功能,而无需等待它们在浏览器中本地实现。

—— 《MDN / CSS Houdini》

如果说 CSS-in-JS 是用现有的标准用 JS 去控制、扩展和实时联动 CSS 的一套方案,那么 CSS Houdini 就相当于进阶版本的 CSS-in-JS,通过公开 CSS 引擎的各个功能,是开发人员能更好的扩展 CSS,笔者认为是不是也可以理解为 CSS Houdini 的出现也代表了现在的纯 CSS 已经很难满足现在日益丰富的 Web 应用。

CSS Houdini

CSS Parser API

这是直接地暴露出 CSS 解析器的 API接口,能够把任意 CSS 类语言解析成为一种中间类型,定义新的结构。

CSS Properties and Values API

CSS Typed OM

CSS Layout API

被设计来提升 CSS 扩展性的 API,该 API 能够让开发者去书写他们自己的布局算法,比如 masonry 或者 line snapping。

**CSS Painting API

Worklets

7 CSS 预处理器 (CSS Preprocessor)

CSS 预处理器是一个能让你通过预处理器自己独有的语法来生成 CSS 的程序。市面上有很多 CSS 预处理器可供选择,且绝大多数 CSS 预处理器会增加一些原生 CSS 不具备的特性,例如代码混合,嵌套选择器,继承选择器等。这些特性让 CSS 的结构更加具有可读性且易于维护。

—— 《MDN / CSS 预处理器》

一些最流行的 CSS 预处理器:

图中看到 PostCSS 的下载量一直遥遥领先其他 CSS 预处理器,PostCSS 比较大的优势在于社区有很多插件可以使用,相当于 CSS 届的 Babel,常见 PostCSS 插件如下:

如果一定需要使用 CSS 预处理器,可能 PostCSS 是最好的选择之一,当然,也是需要看实际你项目的整体方案来选择。

8 CSS-in-JS VS CSS Preprocessor

在 Google Trends 中我们可以看到 2014 年后 CSS-in-JS 的趋势就逐渐超越了 CSS 预处理器,这在一方面也说明了开发人员在 CSS-in-JS 上有着很大兴趣。

二 CSS-in-JS 的介绍

CSS-in-JS是一种样式化技术,其中 JavaScript 用于样式化组件。解析此 JavaScript 时,将生成 CSS(通常作为style元素)并将其附加到 DOM 中。它允许使用JavaScript以声明性和可维护的方式描述样式,从而将 CSS 抽象到组件级别本身。

1 CSS-in-JS 起源历史

CSS-in-JS 的一大特点是它的方案众多,这种看似混乱的状态很符合前端社区喜欢重复造轮子的特征。发展初期,社区在各个方向上探索着用 JS 开发和维护 CSS 的可能性。每隔一段时间,都会有新的语法方案或实现,尝试补充、增强或是修复已有实现。

2 没有 CSS 的那些平台和框架

都是基于各自的设计 imperative & declarative(命令式和声明式)的样式编写,能与程序设计中的各个状态绑定,并不局限于样式表修改这一概念。

3 区别是什么

如果说纯 CSS 框架工具和 CSS-in-JS 的区别是什么,笔者觉得最大的区别就是编译运行的不同时机,我们可以理解成:CSS 框架工具只等于 AOT(Ahead-of-time),CSS-in-JS 则拥有 JIT(Just-in-time) 的能力,例如上面提到的 CSS Houdini API 本质其实也是相当于扩展 CSS 框架的实时运行的能力,而 JIT 的框架和工具本质上也可以使用 AOT 的工具来优化,例如 Babel 和 Webpack。

4 使用 CSS-in-JS 的优点

5 使用 CSS-in-JS 的缺点

6 那些流行的 CSS-in-JS 库

Run-Time(JIT)

运行时动态修改样式的库:

如下图统计,emotion、jss 和 styled-components 都有不错的开发者 NPM 下载使用量,保持长期的增长趋势,这对开发者来说是比较不错的,意味着这些库也有稳定的发展和维护。

逐年递增的下载数量反映了开发社区和使用范围的扩大,也表明了开发者在 CSS-in-JS 上的积极贡献和参与。

这些库大部分的动态修改样式主要使用这几种方式: 1)CSS 样式表

2)CSSOM 修改

这几种方式,笔者比较推荐 CSSOM 修改的方式,页面的 HTML 结构和内容不会变化,也不会有过多的单组件 CSS,而且在修改样式方式上也有很多可以优化和扩展的余地,期望后续的开发者能有优秀的实践可推广。 Build-Time(AOT) 提前编译成 CSS 样式表的库:

提前编译的优势在于一些小程序和其他框架需要 CSS 样式表时是唯一的选择,在用户低端手机和性能上比动态修改样式要更有优势。

7 都有谁在使用?

UI 库

material-ui 是笔者很早关注的一个 material design 的一个开源 UI 组件库,用过 ReactJS 的开发同学可能有了解过,记得一开始官方采用的是内联样式,后续研发了自己的一套 CSS-in-JS 的实现方案,单独发布了 Material-UI 组件中使用的样式方案 —— @material-ui/styles。 公司 数千家公司正在使用 CSS-in-JS 进行开发 Web 应用。

8 Chrome Devtools 对 CSS-in-JS 的支持 在 What's New In DevTools (Chrome 85) 中 Google 更新了 CSS-in-JS 框架的样式编辑的支持。 现在,“Styles”窗格对编辑使用 CSS 对象模型(CSSOM)API 创建的样式提供了更好的支持。许多 CSS-in-JS 框架和库都在底层使用 CSSOM API 来构造样式。 现在也可以使用 “Constructable Stylesheets” 编辑在 JavaScript 中动态添加的样式。

可构造样式表是使用 Shadow DOM 时创建和修改样式的一种新的方法。

例如,(CSSOM API)h1添加的样式 CSSStyleSheet以前不可编辑。现在可以在“Styles”窗格中进行编辑:

三 UI & Code 3.0 新时代

1 自动智能化

在现在前端开发趋势越来越智能化的时代,如果用上 CSS-in-JS 在未来的无论是输出还是输入都有很大的便利性和可控性。 假如把前端和设计的协同工作分为三个时代:

也就是说,可以通过接口、SDK或插件,可以把设计文件的资源和信息读取到代码中,减少人工维护和开发的成本,建立起是设计和程序的桥,方便双方的协同工作。

现如今,很多设计软件都推出了自己的一套插件或 SDK 以供开发者使用,如下图 Sketch 插件的开发:

2 跨平台

CSS-in-JS 在跨平台的优势是比较大的,在不同的系统平台上都有 JS 的 Runtime 的实现,而且 JSON 序列化后的数据也能被更多的平台和语言消费,现在光靠纯 CSS 是无法达到这种通用性和扩展性。

四 展望未来

CSS 设计的初衷是为了全局化的控制样式,通过选择器去扩展丰富实际的页面渲染,而 CSS-in-JS 并不是排斥 CSS 样式,而是说“样式”在现代化的组件颗粒化的发展下,使用 CSS-in-JS 能在瞬息万变的复杂应用场景下更加灵活的解决更多问题。

笔者因早前开发过自己的一套 React UI 库 React-UWP,也基于这套 UI 库做了 CSS-in-JS 的方案,在过去两年中在开发中虽然用的组件不是很多,但是用了 CSS-in-JS 来做整体的样式解决方案,在组件扩展、主题自定义和状态同步有着很大的优势,也期望在后续的社区中有更多优秀的实践可以参考。 如果在文章中发现有误之处,欢迎反馈、纠正。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8