组件级别的 CSS-in-JS

简介: 组件级别的 CSS-in-JS

在 2022 年 11 月 18 日,我们发布了 Ant Design 5.0 的正式版本,同时带入大家视野中的还有 Ant Design 独特的 CSS-in-JS 方案。通过这个方案,Ant Design 获得了相较于其他 CSS-in-JS 库更高的性能,但代价则是牺牲了其在应用中自由使用的灵活性。所以我们把它称为“组件级”的 CSS-in-JS 方案。

 CSS-in-JS 的困境

在 CSS-in-JS 中,hash 会用于确认一段 style 是否已经插入。而计算 hash 的方法通常是将一段完整的 css 转换为 hash 值。比如在 emotion 中,我们检查页面中的元素就可以看到这样的 style 标签,这样的 style 标签对应的 hash 值每一段都是不一样的:


如此便可以引入一个 CSS-in-JS 被诟病已久的问题:我们在编写代码时写的并不是最终的 css,所以每次都需要重新序列化得到 css 后再次计算 hash,这就在每次渲染组件时带来了额外的开销。如果你的页面或者组件带有非常复杂或者大量的 CSS-in-JS 代码,甚至样式会跟随组件的 props 变化,那么这个性能消耗便变得不可忽视。

针对这个问题,各个 CSS-in-JS 库会有自己的应对方式,这里就先不做赘述,让我们来看一看 Ant Design 的方案。

 计算 hash

其实我们不难发现,问题其实在于序列化 css 的过程。如果通过缓存的方法去减少序列化 css 的次数呢?对于应用级的 CSS-in-JS 来说,我们很难去找到一个很合适的 key 去确认缓存。但是如果是组件库的话,最终得到的样式则是比较稳定的。根据我们从 v4 及之前版本确定下来的样式结构,每一个组件的样式在相同的主题变量和相同的版本下是不会改变的。反过来说,只有修改了主题变量,或者改变了 antd 的版本,样式才可能会变化。由此我们得到了一个非常简单的计算 hash 的方法:我们会对所有的 antd 组件应用相同的hash。如此一来,使用 antd 组件时,我们只会对当前的版本和主题变量进行 hash 计算,而前者可以直接由 package.json中得到,后者可以直接从 context 中得到,所以我们并不需要进行繁重的序列化 css 的操作,就可以得到稳定的 hash,从而大幅地减少性能消耗。

 组件缓存

通过上述的方式,我们迈出了『组件级』CSS-in-JS 的第一步,但是这还不够。既然是『组件级』,那我们也可以针对组件再次进行优化。

在 Ant Design 中,一个组件的样式通常来说是“完整”的,也就是说不管这个组件有什么样的变体,他的样式会一并存在于组件样式中。如此我们可以再次得到一个结论:antd 组件的 props 不会影响组件样式。这是非常重要的一点,在应用级的 CSS-in-JS 方案中,由于存在 props 影响组件样式的可能,所以不可避免地会在渲染阶段重新生成组件样式,不管如何去优化这一点仍无法忽视。既然我们采用了“组件级”的方案,那么这个问题就可以很轻松地解决:对组件做样式缓存。在 hash 相同的情况下,同一个组件无论使用了多少次、渲染了多少次,样式永远只会在第一次 mount 时生成一次,剩下的时间里都会命中缓存,这便是『组件级』CSS-in-JS 方案的第二重保险。

 Benchmark

在 Ant Design 5.0 的发布会上,我们简单地做了一次 benchmark,在这里可以做一些补充说明:这个 benchmark 的成立条件是产生一段非常长的不会变更的样式,以此来测试这三个库的基本用法的性能。可以看出在 Ant Design 的“组件级”使用场景下,无论是初次渲染还是二次渲染,antd 都拥有性能上的优势。由于 styled 在处理稳定的样式时有一定优化,所以这个 benchmark 中二次渲染的性能较好,但在有 props 参与样式计算时仍会和 emotion 一样受到重新计算的影响。

 『组件级』的局限

在上述的对比中,其实并不能说 antd 一定优于 styled 和 emotion,而是在 antd 的组件级使用场景下,我们做了相应的优化以取得了性能上的优势。反过来说,由于『组件级』的局限性,antd 的 CSS-in-JS 方案并不能适用于日常构建应用。

由于特殊的 hash 计算方法和组件缓存,在套用 antd 的 CSS-in-JS 方案时,开发者必须自己提供稳定的 hash 和独特的组件名。对于应用来说,像 css module 这样的自动 hash 的能力反而是更为需要的,同时对应用中海量的组件进行缓存也需要额外的管理成本,一旦出错问题是很难排查的。因此我们更加推荐在组件库中使用“组件级”的 CSS-in-JS 方案。

相关文章
N..
|
4月前
|
开发框架 前端开发 UED
Bootstrap的CSS组件
Bootstrap的CSS组件
N..
50 0
|
4月前
|
JavaScript
Vue.js 修饰符:精准控制组件行为
Vue.js 修饰符:精准控制组件行为
|
28天前
|
JavaScript 前端开发 开发者
哇塞!Vue.js 与 Web Components 携手,掀起前端组件复用风暴,震撼你的开发世界!
【8月更文挑战第30天】这段内容介绍了Vue.js和Web Components在前端开发中的优势及二者结合的可能性。Vue.js提供高效简洁的组件化开发,单个组件包含模板、脚本和样式,方便构建复杂用户界面。Web Components作为新兴技术标准,利用自定义元素、Shadow DOM等技术创建封装性强的自定义HTML元素,实现跨框架复用。结合二者,不仅增强了Web Components的逻辑和交互功能,还实现了Vue.js组件在不同框架中的复用,提高了开发效率和可维护性。未来前端开发中,这种结合将大有可为。
91 0
|
29天前
|
前端开发 JavaScript
|
29天前
|
JavaScript 前端开发 测试技术
[译]: Vue.js 函数式组件:what, why & when?
[译]: Vue.js 函数式组件:what, why & when?
[译]: Vue.js 函数式组件:what, why & when?
|
26天前
|
Android开发 iOS开发 C#
Xamarin:用C#打造跨平台移动应用的终极利器——从零开始构建你的第一个iOS与Android通用App,体验前所未有的高效与便捷开发之旅
【8月更文挑战第31天】Xamarin 是一个强大的框架,允许开发者使用单一的 C# 代码库构建高性能的原生移动应用,支持 iOS、Android 和 Windows 平台。作为微软的一部分,Xamarin 充分利用了 .NET 框架的强大功能,提供了丰富的 API 和工具集,简化了跨平台移动应用开发。本文通过一个简单的示例应用介绍了如何使用 Xamarin.Forms 快速创建跨平台应用,包括设置开发环境、定义用户界面和实现按钮点击事件处理逻辑。这个示例展示了 Xamarin.Forms 的基本功能,帮助开发者提高开发效率并实现一致的用户体验。
65 0
|
28天前
|
JavaScript 开发者
揭秘Vue.js生命周期钩子:它们是如何掌控组件的生与死?
【8月更文挑战第30天】Vue.js 的生命周期钩子是开发者必须掌握的关键概念,它涵盖了组件从创建、挂载、更新到销毁的整个过程,提供了在特定时机操作DOM、获取数据或执行逻辑的能力。从 `beforeCreate` 到 `unmounted`,每个阶段都有特定的任务:如 `created` 适合异步数据获取,`mounted` 用于DOM操作,而 `beforeUnmount` 则用于清理资源。
15 0
|
28天前
|
JavaScript 前端开发
揭秘Vue.js组件魔法:如何轻松驾驭前端代码,让维护变得轻而易举?
【8月更文挑战第30天】本文探讨了如何利用Vue.js的组件化开发提升前端代码的可维护性。组件化开发将复杂页面拆分为独立、可复用的组件,提高开发效率和代码可维护性。Vue.js支持全局及局部组件注册,并提供了多种组件间通信方式如props、事件等。通过示例展示了组件定义、数据传递及复用组合的方法,强调了组件化开发在实际项目中的重要性。
17 0
|
2月前
|
编解码 前端开发 容器
CSS Flex布局实战案例:构建响应式卡片组件
【7月更文挑战第17天】通过上述步骤,我们成功地使用CSS Flex布局构建了一个响应式的卡片组件。Flexbox不仅简化了布局代码,还让我们能够轻松实现复杂的布局效果,如响应式设计。在实战中,掌握Flexbox将大大提高前端开发的效率和网页布局的质量。希望这个案例能够帮助你更好地理解和应用Flexbox布局。
|
2月前
|
JavaScript 前端开发 API
前端框架与库 - Vue.js 组件与路由
【7月更文挑战第15天】Vue.js 框架以简洁API和高效DOM更新著名,组件和路由是构建应用的关键。组件是自包含的实例,常见问题包括命名冲突、作用域混淆和状态管理。要避免这些问题,可使用命名空间、明确数据绑定和事件,以及采用Vuex管理状态。Vue Router提供声明式路由,常见挑战包括路由守卫、动态路由参数和懒加载配置。正确使用路由守卫、处理动态参数和实现代码分割能优化路由管理。提供的代码示例展示了基本组件和路由配置。
39 1