浅谈 Web Components
By:moke(司嘉年)
一个有趣的 UI 库
What?
Web Components are a new browser feature that provides a standard component model for the Web, consisting of several pieces: Shadow DOM, Custom Elements, HTML Imports and HTML Templates.
也就是说,Web Components 是 Web 组件模型标准,由浏览器提供原生特性支持,包括Shadow DOM,Custom Elements,HTML Imports 和 HTML Templates
涉及规范
- Custom elements(自定义元素):一组JavaScript API,允许您定义custom elements及其行为,然后可以在您的用户界面中按照需要使用它们。
- https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_custom_elements
- Shadow DOM(影子DOM):一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
涉及规范
- HTML templates(HTML模板):<template> 和 <slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。
- HTML Imports(HTML导入):一旦定义了自定义组件,最简单的重用它的方法就是使其定义细节保存在一个单独的文件中,然后使用导入机制将其导入到想要实际使用它的页面中。HTML 导入就是这样一种机制,尽管存在争议 — Mozilla 根本不同意这种方法,并打算在将来实现更合适的。
- 核心是 组件封装
// 从 video 说起,效果类似于
// 含有该片段的HTML页面将呈现一个功能完整的视频播放器,带播放按钮,进度条,音量调节按钮等等
<video src="./video.mp4" controls></video>
<input
type="text"
value="test"
placeholder="placeholder">
- video、input 相当于浏览器的内置组件,组件视图结构及默认样式藏在 Shadow DOM里,组件逻辑被彻底藏了起来,仅暴露出 autoplay,oninput 等状态 / 行为 Hook 与外界通信
Shadow DOM
document.body.innerHTML = '<div class="container"></div>'
let host = document.querySelector('.container')
let root = host.createShadowRoot()
root.innerHTML = '<p>How <em>you</em> doin?</p>'
- 利用浏览器提供的Shadow DOM特性,我们可以创建自己的Shadow Root:
2. 此时的节点结构是:
<div class="container">
#shadow-root (open)
<p>How <em>you</em> doin?</p>
</div>
3. 可以对 Fragment 做 DOM 操作,相当于一个独立的HTML解析环境,不受外界干扰
Shadow DOM 这款工具旨在构建基于组件的应用。因此,可为网络开发中的常见问题提供解决方案:
- 隔离 DOM:组件的 DOM 是独立的(例如,document.querySelector() 不会返回组件 shadow DOM 中的节点)。
- 作用域 CSS:shadow DOM 内部定义的 CSS 在其作用域内。样式规则不会泄漏,页面样式也不会渗入。
- 组合:为组件设计一个声明性、基于标记的 API。
- 简化 CSS - 作用域 DOM 意味着您可以使用简单的 CSS 选择器,更通用的 id/类名称,而无需担心命名冲突。
- 效率 - 将应用看成是多个 DOM 块,而不是一个大的(全局性)页面。
事件的封装
Shadow Dom 对于事件通过在冒泡阶段 target 的重定向来封装事件,然后一些可能对页面造成影响的事件,Shadow Dom 就会影藏掉这些事件,也就是在冒泡到主页面的过程中被挡住了。
创建流程
- 使用 ECMAScript 2015 类语法创建一个类,来指定web组件的功能使用
- CustomElementRegistry.define() 方法注册您的新自定义元素 ,并向其传递要定义的元素名称、指定元素功能的类以及可选的,其所继承自的元素。
- 使用 Element.attachShadow() 方法将一个shadow DOM 附加到自定义元素上。使用通常的 DOM 方法向shadow DOM 中添加子元素、事件监听器等等。
- 使用 <template> 和 <slot> 方法定义一个HTML 模板。再次使用常规 DOM 方法克隆模板并将其附加到您的 shadow DOM 中。
常见封装类库
- Polymer: Google 推出的 web 组件库,支持数据的单向和双向绑定,兼容性较好,跨浏览器性能也较好;
- X-Tag: 微软推出的开源库,支持 Web Components 规范,兼容 Web ComponentsAPI;
- Slim.js:轻量级的 web 组件库,专注于帮助开发者更好的编写原生web组件,而不依赖于其他框架,但是也提供了良好的拓展性,开发者可以自由拓展。
Polymer 3.0
在 Google I/O 2018 大会上,Google 推出了 Polymer 3.0
Web 组件的生态系统从 HTML Imports 转移到 ES Modules。
包管理系统将支持 npm,更容易将基于 Polymer 的 web 组件和各种工具、框架协同使用。
What's new in 3.0
Vue 与 Web Components
1. Vue 在实现上遵从了部分 Web Components 规范,比如 Shadow DOM 里的 slot:
2. 因为 Web Components 规范尚不成熟,且支持性并不乐观,不用polyfill 就无法投入生产,Vue 依靠构建工具跨过了环境兼容性问题,不依赖浏览器特性支持,但同时也就舍弃了 Shadow DOM 封装性等 Web Components 核心优势
3. 另外,Web Components 是相对底层的组件规范,Vue 除了定义组件规范,还提供了组件通信,数据绑定等上层方案
React 与 Web Components
React 和 web组件 被用以解决不同问题。
Web 组件为可重用组件提供了强大的封装能力,而 React 则是提供了保持 DOM 和数据同步的声明式库。二者目标互补。
可以在 Web 组件里使用 React,或者在 React 里使用 Web Components。
Angular 与 Web Components
Angular 默认情况下没有使用 Web Components 的任何基础设施
不过这里有一个选项问题,就是 Angular 可以设置不同的 ViewEncapsulation,如果是 Native,那会使用到 Shadow DOM 的 API;如果是默认的 Emulated,就不会用到。
总结
Web 「相关」规范设计中一般有一些基本的共识:
- 每一个 Web API 只做一件事
- 同一个功能不会有两个类似的 API 来做
- 保持 ECMAScript 的平台无关性
根本的问题在于,框架的职责在于提供一整套的解决方案,而平台 API 的设计要求是绝不能提供一整套的解决方案,这是无法调和的基本矛盾所在。
总结
1. Web Components 做为浏览器底层特性不应该拿出来和 React 这类应用层框架相比较。
2. 未来 Web Components 会做为浏览器非常重要的特性存在
3. API 偏低层操作,易用性不够,在很长时间内开发者依旧会使用 React / Vue / Angular / Polymer 这样的框架
4. Web Components 可能会做为这些框架的底层来做组件间的互相引用的方法
Thanks
Web Components
By moke
Web Components
- 1,162