一、让人又爱又恨的 Web Components

Web Components 是一种用于构建可重用的 Web 元素的技术。它允许开发者创建自定义的 HTML 元素,这些元素可以在不同的 Web 应用程序中重复使用,并且具有自己的样式、行为和功能。

Web Components 并非一项新技术,而是一组持续演进的、由 W3C 标准化的组件化 API。最早可以追溯到 2011 年左右,大约在 2016 年左右各个浏览器才实现了 Custom Element V1 版本。然而,在同一时期,诸如 Vue 和 React 等组件化框架已经开始主导前端开发生态。

尽管近几年 Web Components 标准和技术都趋于成熟,但早期面临的兼容性问题以及后来 Vue 和 React 等 MVVM 框架的崛起,导致 Web Components 领域一直处于低调状态。接下来,我们将从 Web Components 的发展历程、优势以及开发中面临的挑战三个方面更深入地了解 Web Components。
1、Web Components 发展历程
•2011 年:Google 发布了 Chrome 浏览器,并提出了 “Shadow DOM” 概念,这是 Web Components 的一个重要组成部分。
•2013 年:谷歌工程师 Alex Komoroske 在 Google I/O 大会上首次提出了 Web Components 的概念,并推动了相关标准的制定。
•2014 年:W3C 发布了 Web Components 的规范草案,其中包括四个主要技术:Custom Elements、Shadow DOM、HTML Templates 和 HTML Imports。
•2015 年:Web Components 的规范逐渐得到浏览器厂商的支持,Chrome、Firefox、Safari 等主流浏览器开始逐步实现相关功能。
•2018 年:Web Components 逐渐成为前端开发的主流技术之一,越来越多的开发者开始使用 Web Components 来构建可重用的组件。
•至今:Web Components 技术不断发展和完善,越来越多的框架和库开始支持 Web Components,使其在前端开发中发挥更大的作用。
2、Web Components 有哪些优势
•封装性:Web Components 具有良好的封装性,可以将页面中的功能和样式封装在一个自定义元素内部,避免全局作用域的污染,提高了代码的可维护性和可重用性。
•跨框架兼容:Web Components 是基于 Web 标准的技术,可以在任何支持 Custom Elements 和 Shadow DOM 的现代浏览器中使用,与各种前端框架和库兼容性良好。
•标准化:Web Components 的规范由 W3C 组织制定,具有较高的标准化程度,有利于统一前端开发的规范和实践,提高了代码的可维护性和可移植性。
•性能优势:通过使用 Shadow DOM 技术,Web Components 可以实现更好的性能优化,避免不必要的重绘和重排,提高页面的渲染效率和用户体验。

目前,前端开发中有许多流行的框架可供选择,如 React、Vue、Angular、Solid、Svelte、Preact 等。框架的选择是一个复杂的决策过程。当框架版本升级时,项目可能需要面临重构的问题。例如,从 Vue2 升级到 Vue3 可能需要付出较大的改造成本。如果不进行升级,就无法使用最新特性,甚至可能面临框架旧版本不再维护的尴尬局面。在这种情况下,Web Components 的框架无关性可以在一定程度上改善这种局面。

就性能而言,以将 React 组件转换为 Web Components 为例,可以优化执行过程,减少阻塞,提高页面性能。在普通的 React 组件中,初次执行时需要一次性完成所有必须的节点逻辑,这些逻辑的执行会同步占用在 JavaScript 的主线程上。当页面变得足够复杂时,一些非核心逻辑可能会阻塞后面核心逻辑的执行。而通过使用 Web Components 优化 React 组件,执行过程会变得更简洁。例如,注册一个复杂的逻辑组件时,在 React 执行时只需执行一个 createElement 语句,创建组件只需要 1-2 微秒即可完成。真正的逻辑不会立即执行,而是等到 “核心任务” 执行完毕后再执行,甚至可以在合适的时机再执行,从而降低 Diff 成本。




3、使用 Web Components 开发的痛点

虽然 Web Components 具有许多令人喜欢的优点,但在实际开发中也存在一些棘手的问题。以下是几个典型问题:
•原生开发难题:Web Components 是一项原生技术,因此在组件编写过程中需要回归到原生开发。事件处理、状态管理等方面都需要自行处理。尽管大多数框架都提供了对 Web Components 的封装方案,但使用这些方案又需要引入相应框架的运行时,这导致脱离了框架无关性这一最大优势。
•Form 表单问题:在 Shadow DOM 中,包含、或<select>等标签的 value 不会自动与表单关联,因此在表单提交时无法获取相应的 value,需要手动处理,另外 ElementInternals 提案 也在致力于解决这个问题。<br> •样式隔离问题:虽然样式隔离有助于避免全局样式污染的问题,但在某些场景下,如主题颜色等,仍需要将样式同步到 Web Components 组件内部。目前可以通过 CSS 变量实现主题切换。此外,需要注意的是,slot 插槽中的 DOM 元素样式并未得到隔离。</select>

针对这些问题,开发人员可能需要考虑采取一些额外的措施来解决挑战,以确保 Web Components 在实际应用中能够更加顺畅地运行。
二、目前主流的 Web Components 组件方案
1、头部案例

Twitter

Twitter 2016 年开始将自己的嵌入式推文从 iframe 切换成 ShadowDOM,减少了内存消耗、加快了渲染速度,并批量渲染的时候保持丝滑。Upcoming Change to Embedded Tweet Display on Web

Youtube

Youtube 作为 google 系的产品,很早就在全站用上了 Web Components,并且开源了自己播放器组件 GitHub - GoogleWebComponents/google-youtube: YouTube video playback web component 此外 google 开源的 Web Components 还是很多的,Google Web Components · GitHub ,包括地图、drive、日历等等。

Github

Github 对 Web Components 的使用很早,具体可以看: How we use Web Components at GitHub | The GitHub Blog,2017 年 Custom Elements v1 版本在 chrome 和 safari 上相继实现之后,Github 开始大范围使。要知道 Github 2018 年才刚刚完全移除 jQuery:Removing jQuery from GitHub.com frontend | The GitHub Blog 这既得益于 Github 自身项目组件化的架构,也得益于 Web Components 本身与框架无关的特性非常识合作老项目升级。

Adobe Spectrum

Adobe Spectrum 是由 Adobe 创建的设计系统,该站点是一个基于 Web Components 的 UI 框架产品。
2、方案对比

目前主流的 Web Components 组件方案有三种:

方案一:
•特点:以 React 和 Vue 为代表,通过将 React 或 Vue 组件包装为 Web Components 组件的方式实现。
•优点:利用了本身框架的特性如生命周期、状态管理等,易于开发者使用。
•缺点:需要引入本身框架的运行时,导致组件体积增加,同时丧失了框架无关性这一优势。

方案二:
•特点:以 Stencil 和 LitElement 为代表,提供了专门的编译器、工具链和语法糖来构建 Web Components。
•优点:相比第一种方案,引入本身框架的运行时可能更小,减少了体积。
•缺点:需要学习新的语法和工具,可能增加开发者的心智负担。

方案三:

特点:以 Svelte 和 Solid 为代表,直接将组件编译成原生 Web Components

优点:放弃了虚拟 DOM,利用编译或转译能力直接生成操作 DOM 的更新函数,性能优秀,接近原生 DOM。另外 Vue3 中的 Vapor 模式,正是借鉴了这种模式,目前正在试验阶段。
三、与 SolidJS 结合的 “化学反应”
1、SolidJS 有何不同

SolidJS 是一个快速、灵活、可扩展的 JavaScript 库,用于构建用户界面。与其他前端框架相比,SolidJS 有一些独特的特点和优势:
•Reactivity System: SolidJS 使用基于数据变化的响应式系统,可以精确追踪状态的变化,并只更新发生变化的部分,从而提高性能。
•Fine-grained Reactivity: SolidJS 提供了细粒度的响应式更新,可以在组件级别、元素级别甚至属性级别进行更新,避免不必要的重新渲染。
•No Virtual DOM: 与其他框架不同,SolidJS 不使用虚拟 DOM,而是直接编译生成操作真实 DOM 的函数,减少了 diff 算法的开销,提高了性能。
•Hooks-based API: SolidJS 使用类似 React Hooks 的 API,使得组件逻辑更易于复用和组合
2、可以解决哪些问题
•直接在编译阶段生成原生 Web Components,核心库非常小巧,没有额外的依赖,可以帮助减少项目的体积。
•提供响应式状态管理、事件管理、生命周期等,解决原生开发的痛点。
•类 React 语法,上手容易,降低开发者心智负担,转换成 Web Components 十分流畅。
四、从 0 到 1 搭建 Aura Design Web Components 组件库
1、工程目录设计

该项目采用了 Monorepo 设计,旨在统一管理各个子项目,避免开发阶段频繁发布/安装 npm 包来同步代码。具体内容包括:
•packages/eslint-config-aurai: 用于管理 ESLint 配置,集成了 TypeScript 和 Prettier(用于 JavaScript/TypeScript 格式化)。
•packages/stylelint-config-aurai: 用于管理 StyleLint 配置,集成了 stylelint-order(用于样式属性排序)和 Prettier(用于样式格式化)。
•packages/aura-design: 基于 eslint-config-aurai 和 stylelint-config-aurai 规范,使用 Solid 和 Solid Element 构建的 Web Components 元组件库,包括按钮、图标、卡片、布局等基础组件。
•packages/aura-design-pro: 与 aura-design 类似,但封装了一些复杂组件,通常是依赖第三方库的组件,例如支持 Markdown 渲染的富文本组件、视频播放组件等。
•apps/aura-design-docs: 基于 Storybook 的组件文档库,支持查看代码、组件预览,并可实时预览修改组件属性。
•apps/react-starter、apps/vue-starter: 计划用于验证 Web Components 在 React 和 Vue 组件中的兼容性等问题。




2、部分细节展示
2.1、组件样式编写

Web Components 组件中的样式是内联到每一个组件的 Shadow DOM 中,因此不能用常规的 CSS Modules 等方案。本项目中使用了大约 4 种不同的内联样式方案,以对应不同的应用场景:
•原子化 CSS 方案:UnoCSS 致力于将所有样式属性细分为最小单元的 CSS,以便实现最大程度的自由组合和复用。此外,UnoCSS 还具有自动分析文件中使用的样式的功能,只将实际使用的样式打包,从而减小文件大小。




•CSS inline 模式:利用 Vite 的功能,将外部样式作为内联模式引入,当需要自定义复杂样式或语义化场景时,可能会使用此模式。

import styles from './Button.css?inline';

•CSS Template:CSS 模板字符串类似于 CSS-in-JS 方案,通常用于根据 prop 变量动态控制样式。尽管这种方法会增加一定的运行时处理逻辑,但可以有效减少 CSS 重复和文件大小。

const styles = css
:host {
display: inline-block;
}
.box {
height: ${props.size + 2}px;
line-height: ${props.size + 2}px;
position: relative;
overflow: hidden;
}
.nums-chip {
transition: transform 1.5s;
transform: translate(0, -50%);
overflow: hidden;
}
.number {
font-size: ${props.size}px;
line-height: ${props.size + 2}px;
}
;

•CSS Link:使用 link 标签引入外部样式表,当组件样式比较多、体积较大时,如果直接内嵌到组件内部,当有 N 个组件时,体积就会增大 N 倍(暂不考虑浏览器内置优化逻辑),而使用 link 标签减少体积同时也可以利用上浏览器缓存的特性。

...
return (
<>

{styles}


</>
);
...

2.2、Svg 图标集成

Icon 组件引入和注册

import {
defineCustomElements,
registerIcon,
Icon,
} from '@aura-group/aura-design';
registerIcon('/iconfont.svg'); // 自定义 svg 图标注册,使用主项目根目录的 svg 图标文件,如果不注册则使用默认图标
defineCustomElements({ Icon }); // 注册 Web Components 图标组件

组件使用

2.3、组件打包

支持 ES(按需加载)和 UMD 两种范式,自动生成 Typescript 声明文件:

package.json

{
"name": "@aura-group/aura-design",
"version": "0.3.9",
"description": "",
"main": "dist/aura-design.umd.js",
"module": "dist/aura-design.es.js",
"typings": "dist/types/components/index.d.ts",
"files": [
"dist"
],
"type": "module",
...
}

Vite 相关配置

...
build: {

lib: {
entry: 'src/components/index.ts',
name: 'aura-design',
fileName: (format) => aura-design.${format}.js,
},
},
...

3、基于 Storybook 的组件文档

Storybook 是一个开源工具,用于开发和展示 React、Vue、Angular 等前端组件的交互式 UI 组件库。通过 Storybook,开发人员可以在一个独立的环境中编写、展示和测试组件,而不必依赖于整个应用程序。这样可以更快地开发和调试组件,同时也方便团队成员之间的协作。

Storybook 提供了一个交互式的界面,开发人员可以在其中创建不同的 “故事”(stories),每个故事对应一个组件的不同状态或交互方式。通过 Storybook,开发人员可以轻松地查看和测试组件在不同状态下的表现,从而更好地理解和调试组件的行为。

Aura Design 组件库文档




4、未来规划

目前,我们的组件库中包含了 16 个基础组件和一些与 AI Chat 相关的组件。在 A-M 网站中,95% 的组件都源自 Aura Design 组件库。未来,除了继续封装一些基础组件外,我们将优先开发常规组件库中缺失的比如 AI 应用的相关组件。随着组件库的逐步丰富和条件的成熟,我们会考虑将其开源。如果您有任何相关组件的封装需求或希望成为项目的成员,请随时与我联系。


↙↙↙阅读原文可查看相关链接,并与作者交流