Web Components浅析
Web Components 是一种使用封装的、可重用的 HTML 标签、样式和行为来创建自定义元素的 Web 技术。
Web Components 自己本身不是一个规范,而是一套整体技术,包含下面3个独立规范:
- Custom Elements:允许开发者定义自己的 HTML 标签(考虑SEO,还是语义化为好)。
- Shadow DOM:用于封装样式和标记,不受外部 DOM 影响——天然自带Scope能力(本质是一组 JS API)。
- HTML Templates (<template> 和 <slot>):声明式的重用 HTML 代码片段。
但是,我觉得还应该包含:ES Modules。
Custom Elements 和 Shadow DOM 的可靠性是确定的,毕竟是标准的一部分。
Custom Elements
在2008年W3C 发布了第一个HTML公开草案,其是就是可以使用自定义标签的——2000年W3C准备用XHTML来替代HTML4,结果被抛弃!
XHTML,或者更准确地说是 XHTML 1.0,是一种基于 XML 的标记语言,旨在在网页设计中取代HTML 4.01。它由 W3C 推出,其规范在2000年1月成为官方的推荐标准。XHTML1.0实际上是HTML 4.01的严格版本,并要求开发者遵循更加严格的语法规则——XHTML基于XML,它对标记的正确性有更高的要求:
- XHTML 元素必须被正确地嵌套。
- XHTML 元素必须被关闭。
- 标签名必须用小写字母。
- XHTML 文档必须拥有根元素。
- XHTML需要开发者在文档开头声明正确的DOCTYPE,而在实际的实践中,由于历史原因和混乱的标准,很多时候开发者并没有遵循正确的声明,导致页面以兼容模式而不是标准模式渲染。
- 为了正确地作为XHTML传送,Web服务器需要设置MIME类型为application/xhtml xml。不幸的是,一些浏览器对这种MIME类型的处理不理想,这使得开发者们更倾向于使用更通行的text/html,这实际上使XHTML变成了浏览器中被当作HTML解析的标记语言。
在 HTML5 之前,使用非标准标签通常会被视为不良实践,因为这可能导致不可预测的行为,尤其是在不同的浏览器之间。
然而,HTML5 引入了一种更加宽容的解析规则,允许这些非标准标签存在,浏览器不会因为碰到未知标签而破坏整个页面。即使如此,这些自定义标签没有任何默认的样式或行为,它们就像普通的 HTML 元素(默认为内联元素),除非通过 CSS 或 JavaScript 给予样式和行为。
自定义标签和自定义元素是两个相关但不同的概念。它们代表着 web 开发中自定义组件的不同方面和不同阶段的发展。
自定义标签与自定义元素
自定义标签(非标准标签)
自定义标签:Custom Tags、Non-standard Tags、User-defined Tags……
自定义标签仅在语义上是自定义的,而没有附加任何特殊的行为;
自定义元素(Custom Elements)
自定义元素是 Web Components 规范的一部分,它允许开发者创建完全定制化和可重用的 HTML 元素。
与仅仅创建一个新的标签名不同,自定义元素能够拥有自己独特的行为和属性。
Custom Elements 规范定义了如何注册新的元素、如何附加行为、以及如何处理元素的生命周期事件(如创建、连接到文档、断开连接和属性更改时)。
自定义元素通常使用 customElements.define() 方法在 JavaScript 中注册,这样,当元素被添加到 DOM 时,就会与一个 JavaScript 类关联起来。这个类继承自 HTMLElement,允许它具备 DOM 接口的所有特性,并添加自定义的逻辑和样式。这意味着自定义元素不仅仅是形式上的定制,而是实现了真正的封装和功能拓展。
区别总结
- 语义:自定义标签仅在语义上是自定义的,而没有附加任何特殊的行为;相反,自定义元素通过 Custom Elements API 注册,并可以包括复杂的逻辑和状态。
- 功能性:自定义元素支持完整的生命周期管理,提供创建时、附加到 DOM、属性变动等时机的钩子,而自定义标签则没有这些功能。
- 标准化:自定义元素是 Web Components 的官方标准之一,得到了浏览器的广泛支持;而自定义标签顾名思义,是非标准的,它们允许存在,但并不是 HTML 规范的一部分。
- 兼容性:自定义元素需要浏览器支持相关的标准,虽然现在大多数现代浏览器都提供了支持,但在一些旧的浏览器中可能需要 polyfills;而自定义标签通常哪种浏览器都能解析,只是作为普通的元素看待。
custom element生命周期
在custom element的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用:
- connectedCallback:当 custom element首次被插入文档DOM时,被调用。
- disconnectedCallback:当 custom element从文档DOM中删除时,被调用。
- adoptedCallback:当 custom element被移动到新的文档时,被调用。
- attributeChangedCallback: 当 custom element增加、删除、修改自身属性时,被调用。
具体参看:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components/Using_custom_elements
Shadow DOM
Shadow DOM 主要目的:封装与隔离——相比iframe、frame更加轻量级。
- IFrame是一个独立的html页面,shadow DOM是当前html页面的一个代码片段,
- 不需要创建额外的渲染环境——不需要创建一个完整的文档环境,而是基于现有的上下文中创建封闭的DOM结构。
Shadow DOM接口是关键所在:它可以将一个隐藏的、独立的DOM附加到一个元素上,它以shadow root节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的DOM元素一样,但是这棵子树不在主DOM树中——即影子DOM是一种不属于主DOM树的独立的结构,所以Shadow DOM内部的元素始终不会影响到它外部的元素(除了:focus-within),这就为封装提供了便利!
- Shadow host: 一个常规DOM节点,Shadow DOM会被附加到这个节点上。
- Shadow tree: Shadow DOM内部的DOM树。
- Shadow boundary: Shadow DOM结束的地方,也是常规DOM开始的地方。
- Shadow root: Shadow tree的根节点。
Shadow DOM都不是一个新事物,在过去的很长一段时间里,浏览器用它来封装一些元素的内部结构,以一个有着默认播放控制按钮的<video>元素为例,我们所能看到的只是一个<video>标签,实际上,在它的Shadow DOM中,包含来一系列的按钮和其他控制器。
其结构如下:
而现在,我们可以来自己制造相关的标签(如video类似的功能模块)
怎么使用Shadow DOM
看这个就好:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components/Using_shadow_DOM
但是,https://github.com/Tencent/omi等框架应该更加适合你!
HTML templates(HTML 模板)
这个用过vue的理解应该不难:
- <template>包含一个 HTML 片段,不会在文档初始化时渲染。<slot>插槽,类似占位符,可以填充自己的内容。、
- <slot>插槽,类似占位符,可以填充自己的内容。
但是真的要用的话,还是用omi等类似的框架。
为什么不用原生API
这个问题就是,为什么要用jQuery?为什么放弃jQuery使用vue或react?
其是Web Components 了解一下就好。
为什么不推荐使用Web Components
React 和 Vue 在组件化开发方面有自己的实现,并没有直接采用 Web Components 作为内部实现——不过,它们两者都提供了与 Web Components 兼容的接口。
下面是我个人感觉他们放弃Web Components的原因:
React放弃Web Components
- 封装性:React 组件经常需要和一个复杂的状态以及生命周期方法交互,这些都不是 Web Components 标准提供的。
- 性能优化:React 的虚拟 DOM 可以通过最小化实际的 DOM 操作来提升性能,这一点在批量更新 UI 或大型应用中尤为明显。
- 跨平台:React 不仅仅用于 web 开发(通过 React Native,它也被用于移动应用开发),而 Webb Components 专注于 web 标准和浏览器环境。
- 生态系统:React 拥有非常庞大且成熟的生态系统,包括状态管理(如 Redux)、路由(如 React Router)等各种工具和库。
Vue3放弃Web Components
- 响应式系统:Vue 的响应式系统使得数据和视图能够自动同步更新,而 Web Components 没有内建这样的响应式机制。
- 模板语法:Vue 通过其简洁的模板语法扩展了普通的 HTML,使开发者可以更加容易地描述复杂的 UI 结构,而 Web Components 使用的是普通 HTML 搭配 JavaScript。
- 工具链支持:Vue CLI 提供了非常强大的工具链支持,包括项目脚手架、开发服务器、热重载等,而这些在 Web Components 中不是直接可用的。
- 生态系统:与 React 类似,Vue 也拥有广泛的插件和支持库,例如 Vuex、Vue Router 等,这些让 Vue 应用开发更为完善。
尽管 React 和 Vue 没有直接采用 Web Components 作为内部实现,但它们都提供了对 Web Components 的支持
2011年,Alex Russel首次提出了Web Components的概率并首次演示了demo,这时候整套技术包括三个方面:scoped css,shadow DOM和Web components。W3C也在此时开始推进Web Components规范。 2012年,HTML Template很快被实现,作为wrapper包裹内容,在页面加载时不使用,在之后运行时实例化。同时Shadow DOM V0标准发布并被实现,并且Ember和Angular开始计划支持Web Components,甚至基于它去做改造,但最终没有结果。 至2018年,Web Components在主流浏览器中均被支持,但是并未达到普及程度,具体参看:https://caniuse.com/?search=Web Components
但是,比如视频播放器、sql编辑器等超大件,还是非常适合Web Components的。不过这里还是推荐使用框架来做。
vue3项目Web Components案例
Vue 和 Web Components 是互补的技术,具体可以看官方文档:https://cn.vuejs.org/guide/extras/web-components
Vue 在 Custom Elements Everywhere 测试中取得了 100% 的分数。在 Vue 应用中使用自定义元素基本上与使用原生 HTML 元素的效果相同! Vue 提供了一个和定义一般 Vue 组件几乎完全一致的 defineCustomElement 方法来支持创建自定义元素。这个方法接收的参数和 defineComponent 完全相同。 import { defineCustomElement } from 'vue' const MyVueElement = defineCustomElement({ // 同平常一样的 Vue 组件选项——正常的 Vue 组件选项都有 props: {}, emits: {}, template: `...`, // defineCustomElement 特有的:注入进 shadow root 的 CSS styles: [`/* inlined css */`] }) // 注册自定义元素,注册之后,所有此页面中的 `<my-vue-element>` 标签 都会被升级 customElements.define('my-vue-element', MyVueElement) // 你也可以编程式地实例化元素(必须在注册之后) : document.body.appendChild( new MyVueElement({ // 初始化 props(可选) }) ) // 也可以直接使用 export default defineComponent({ setup(){ return ()=>(<my-vue-element/>) } }) 这样用,是不是非常爽!
vue3 使用Web Components需要注意的点:
failed to resolve component
默认情况下,Vue 会优先尝试将一个非原生的 HTML 标签解析为一个注册的 Vue 组件,如果失败则会将其渲染为自定义元素。这种行为会导致在开发模式下的 Vue 发出“failed to resolve component”的警告。所以需要告诉 Vue 将某些确切的元素作为自定义元素处理并跳过组件解析。在 vite.config.ts 配置:
代码语言:javascript复制{resolve: {
alias: {//组件提供模板选项,但是在 Vue 这个构建中不支持运行时编译
'vue': 'vue/dist/vue.esm-bundler.js',//需要配置你的 bundler 别名 vue: vue/dist/vue.esm-bundler.js
'@': resolve(__dirname, 'src')
}
},
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: tag => tag.startsWith('cus-')// 以 cus- 开头的作为自定义元素处理
}
}
})]}
Provide / Inject API
Provide / Inject API 和相应的组合式 API 在 Vue 定义的自定义元素中都可以正常工作。但是请注意,依赖关系只在自定义元素之间起作用。
但是为推荐费必要
插槽
在组件内部,可以像往常一样使用 <slot/> 渲染插槽。但是在解析最终生成的元素时,它只接受原生插槽语法:
- 不支持作用域插槽。
- 传递命名插槽时,请使用 slot attribute 而非 v-slot 指令
React项目Web Components案例
说实话,react原生来写干嘛呢?
如果要在react项目里面写,推荐使用 https://lit.dev/
或者使用https://github.com/Tencent/omi/ 来写个项目,打包成组件库,然后再业务里面使用!
Web Components 生态
Lit:Lit是一个轻量的库,但它依然保留了web组件的所有特性。
Omi:Web Components 框架.
Vaadin: Vaadin 是以java作为开发语言的前端框架,它提供了一套以Web Components为基础的丰富的企业级UI组件库,关键他和spring结合的非常爽,比GWT用起来。
Ionic Framework: 本来是为Angular构建的(4.x适配Angular、Vue 、React),Ionic4 Web端基于Web Components——具有更好的运行速度,相比以前版本的Ionic框架性能提升很多!优异的性能则让 Ionic 成为了构建高性能 PWA 的最佳 UI 框架。
说实话吧,Web Components 相比周边生态还是没有起来。可以作为大型项目某些模块的补充技术!
svelte:前端框架新秀,原生支持Web Components。个人不是很了解,跳过!
Google 从 2013 年开始一直在持续推进的基于 Web Components 封装的类库,同时还开放了基于 Polymer 开发的组件集合 PolymerElements · GitHub 和开发周边。 2015 年 Google 正式发布 Polymer 1.0 ,注意时间点,当时还是Custom Elements v0 版标准 2017年Custom Elements v1 版标准在各大浏览器落地,Polymer 发布了 2.0,并且不再封装 Custom Elements API,不再默认使用shadowDOM、目标兼容各种框架,开始变成轻量级类库。
- 由于起步几乎最早以及 google 背书,可能到现在也是影响用户数最大的 Web Components 基础库,Youtube 基于Polymer 对整站做了重构,Google 很多产品包括 Android 和 ChromeOS 平台也都用了 Polymer。
但个人觉得总体上相比与彼时流行的其他框架 Polymer 还是不温不火,Google 似乎也有同感、随着 Polymer 的轻量化升级,于是在 2018 年又发布了更现代化的 lit GitHub - lit/lit: Lit is a simple library for building fast, lightweight web components.包括 lit-html 模板渲染库 lit/packages/lit-html at main · lit/lit · GitHub和基于 lit-html 的 lit-element lit/packages/lit-element at main · lit/lit · GitHub 创建 Web Component 的 base class 。 Lit-html 基于 ES 的模板自变量和 template 标签,用注释节点去动态填充,没有JSX 转换虚拟 dom的过程,把大部分模板创建渲染的事都交给浏览器去做,提供了轻量的 api 让我们可以在 JS 中写 HTML-Templates。 Lit-Element 的 Reactive properties 、Scoped styles 等面向现代化 JS 语法的特点让他现在很受欢迎。 Google 推荐新用户使用 lit,但也将 Polymer 推到了 3.0 版本,放弃了 HTML Imports 转向 JS modules,并且支持 Polymer 跟 lit 混用,目前持续又维护和支持,Slack Channel 上一直很活跃。 除了 Google 自己, 微软的 PWA stater GitHub - pwa-builder/pwa-starter: Welcome to the PWABuilder pwa-starter! Looking to build a new Progressive Web App and not sure where to get started? This is what you are looking for!,选择 lit 框架和 封装的 Web Components 作为基础库。 Adobe 基于 LitElement 封装并开放了 Spectrum Web ComponentsSap 基于 Lit-html 封装并开源了 ui5-webcomponents/02-custom-UI5-Web-Components.md at master · SAP/ui5-webcomponents · GitHubRed hat GitHub - 1-Platform/op-components: One platform component library.等众多公司使用了 lit 开发自己的组件库或平台。 另一个类库 GitHub - skatejs/skatejs: Effortless custom elements powered by modern view libraries. 也是基于 lit-html 的。
Web Components 头部案例
目前生成环境使用Web Components 的案例有这些(非全部使用!)
Twitter 2016 年开始将自己的嵌入式推文 从 iframe 切换成 ShadowDOM,减少了内存消耗、加快了渲染速度,并批量渲染的时候保持丝滑。Upcoming Change to Embedded Tweet Display on Web
Youtube
Youtube 作为 google 系的产品,很早就在全站用上了 Web Cmponents,并且开源了自己播放器组件 GitHub - GoogleWebComponents/google-youtube: YouTube video playback web component此外 google 开源的 Web Components 还是很多的,Google Web Components · GitHub ,包括地图、drive、日历等等。
Google Earth:
Google Earth 的网页版使用了Web Components技术来创建用户交互界面的一部分。
EA
EA 的游戏工作室分布在全球各地,为了保证不同团队和工作室的设计开发体验统一,EA 基于 Web Components 构建了自己的 Network Design System,同时也支持这自己的 UIaaS。
Github
github 对 Web Components 的使用很早,具体可以看: How we use Web Components at GitHub | The GitHub Blog2014 年 Custom Elements v0 specification 出现的时候 github 就开始关注:Search · topic:web-components org:github · GitHub,并且开源了其中一系列 Web Components GitHub - github/github-elements: GitHub’s Web Component collection.2017 年 Custom Elements v1 版本在 chrome 和 safari 上相继实现之后,github 开始大范围使用
要知道 github 2018 年才刚刚完全移除 jqueryRemoving jQuery from GitHub.com frontend | The GitHub Blog 这既得益于 github 自身项目组件化的架构,也 Web Components 本身与框架无关的特性非常识合作老项目升级。
github 还开源了 用于开发Web Components 的库 Catalyst:GitHub - github/catalyst: Catalyst is a set of patterns and techniques for developing components within a complex application.
而他的思路借鉴了 Stimulus 和 LitElement。
- 既然提到了 Stimulus,就叉开讲讲这个东西,Stimulus 很适合对老项目改造,尤其是 ruby on rails、jsp 服务端渲染、没有 webpack 之类的前端工具链,技术栈多且混乱的项目。Stimulus 的思路就是通过 MutationObserver 监控元素的变化, 然后取元素、补绑事件或者修改引用。他的定位就很轻盈,就是配合HTML页面,提供动态交互支持,不像现在的很多框架,动辄就是整站重写。
同时 github 还开源了一个 View Component 框架用来在 ruby on rails 里面构建同构应用GitHub - github/view_component: A framework for building reusable, testable & encapsulated view components in Ruby on Rails.
SalesForce
SalesForce 作为一家 ToB 服务的公司,面对各种不同技术栈的客户,选择 Web Components 原因有两点,一是需要一套统一的通用组件面向所有客户,二是在很多特定领域,很多客户很难对他们的传统技术体系做大规模升级,而引入 Web Components 可以避免这类技术改造风险。
他们开源了自己的 Web Components 组件库 Component Library,并提供一整套基于 的企业级研发工具 GitHub - salesforce/lwc: LWC - A Blazing Fast, Enterprise-Grade Web Components Foundation除了通过 LWC,让客户可以在自己的环境中基于组件库配置、开发、部署应用,SalesForce 还开放了自己的 SalesForce 工作平台 ,平台为所有客户提供一站式配置、部署和升级的能力。
Oracle
Oracle 在 2017 年开始在自己的 GitHub - oracle/oraclejet: Oracle JET is a modular JavaScript Extension Toolkit for developers working on client-side applications. 构建工具中增加了对 CustomElement 的支持,在此之前是用的是 jQueryUI。Oracle 对 WebComponents 对态度其实很值得 ToB 同行学习,他并没有刻意想拜托 jQuery,而是让 WebComponents 与现有的 jQuery、Knockout 并行使用,只在新功能上推进 WebComponents ,保持老项目稳定,在历史遗留和新技术之间保持了合理的平衡。而在 jet 的生态方面,他们也在持续建设 Web Component 驱动的共享组件中心 Building the future of Oracle JET Ecosystem | by João Tiago | Digital Transformation Research Group | Medium
ING:
荷兰国际集团(ING)在他们的网站和网上银行平台中大量使用了 Web Components,他们通过使用 Lion Web Components 库共享跨项目的UI组件。
Comcast:
Comcast 的 Xfinity产品线中的某些web应用使用了 Web Components。
Adobe Spectrum:
该站点是一个基于 Web Components 的 UI 框架产品
参考文章:
神奇的Shadow DOM https://jelly.jd.com/article/6006b1045b6c6a01506c87ac
Vue3.2 实现 Web Components https://ainyi.com/125
https://www.albertaz.com/blog/web-components-ststus
转载本站文章《Web Components从技术解析到生态应用个人心得指北》, 请注明出处:https://www.zhoulujun.cn/html/webfront/SGML/htmlBase/2012_0823_9020.html