Web Components 是 WHATWG 和 W3C 正在尝试的 Web 组件化方案,为组件化的 Web 前端开发提供浏览器级别的支持。最早的努力当数 “Web Component” 规范说明 circa 2011 的出现,并在同年的 Fronteers Conference 大会上由 Alex Russell 将之宣之于众。该 Web Component 规范的产生和发展,旨在提供一种权威的、浏览器能理解的方式来创建组件。
Web Components 是一系列 Web 技术的集合,主要包括 Shadow DOM、Custom Elements、HTML templates、HTML Import(废弃,HTML Modules替换)。
主要的技术与兼容性
Custom elements:可重用网络组件。
HTML 现行标准缺乏自动关联 JS 行为和标记的方法。自定义元素使 HTML 变得现代化;补充了缺少的部件,并将结构与行为相结合。 如果 HTML 无法为问题提供解决方案,我们可以创建自定义元素来解决。 自定义元素在保留 HTML 优点的同时为浏览器带来新功能。
有关创建自定义元素的规则
自定义元素的名称必须包含短横线 (-)。因此,、 和 等均为有效名称,而 和 则为无效名称。这一要求使得 HTML 解析器能够区分自定义元素和常规元素。它还可确保向 HTML 添加新标记时的向前兼容性。
// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});
``` html
<app-drawer></app-drawer>
拓展自定义元素
class FancyDrawer extends AppDrawer {
constructor() {
super(); // always call super() first in the constructor. This also calls the extended class' constructor.
...
}
toggleDrawer() {
// Possibly different toggle implementation?
// Use ES2015 if you need to call the parent method.
// super.toggleDrawer()
}
anotherMethod() {
...
}
}
- 扩展原生 HTML 元素
``` javascript
// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class FancyButton extends HTMLButtonElement {
constructor() {
super(); // always call super() first in the constructor.
this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
}
// Material design ripple animation.
drawRipple(x, y) {
let div = document.createElement('div');
div.classList.add('ripple');
this.appendChild(div);
div.style.top = `${y - div.clientHeight/2}px`;
div.style.left = `${x - div.clientWidth/2}px`;
div.style.backgroundColor = 'currentColor';
div.classList.add('run');
div.addEventListener('transitionend', e => div.remove());
}
}
customElements.define('fancy-button', FancyButton, {extends: 'button'});
<!-- This <button> is a fancy button. -->
<button is="fancy-button" disabled>Fancy button!</button>
扩展原生元素时,对 define() 的调用会稍有不同。所需的第三个参数告知浏览器要扩展的标记。这很有必要,因为许多 HTML 标记均使用同一 DOM 接口。例如,<section>、<address> 和 <em>(以及其他)都使用 HTMLElement;<q> 和 <blockquote> 则使用 HTMLQuoteElement;等等。指定 {extends: 'blockquote'} 可让浏览器知道您创建的是增强的 <blockquote> 而不是 <q>。有关 HTML DOM 接口的完整列表,请参阅 HTML 规范。
Shadow DOM 解决了构建网络应用的脆弱性问题。脆弱性是由 HTML、CSS 和 JS 的全局性引起的。
Shadow DOM 修复了 CSS 和 DOM。它在网络平台中引入作用域样式。 无需工具或命名约定,您即可使用原生 JavaScript 捆绑 CSS 和标记、隐藏实现详情以及编写独立的组件。
Shadow DOM 这款工具旨在构建基于组件的应用。因此,可为网络开发中的常见问题提供解决方案:
隔离 DOM:组件的 DOM 是独立的(例如,document.querySelector() 不会返回组件 shadow DOM 中的节点)。
作用域 CSS:shadow DOM 内部定义的 CSS 在其作用域内。样式规则不会泄漏,页面样式也不会渗入。
组合:为组件设计一个声明性、基于标记的 API。
简化 CSS - 作用域 DOM 意味着您可以使用简单的 CSS 选择器,更通用的 id/类名称,而无需担心命名冲突。
效率 - 将应用看成是多个 DOM 块,而不是一个大的(全局性)页面。
Shadow DOM 与普通 DOM 相同,但有两点区别:
1) 创建/使用的方式;
2) 与页面其他部分有关的行为方式。
通常,您创建 DOM 节点并将其附加至其他元素作为子项。 借助于 shadow DOM,您可以创建作用域 DOM 树,该 DOM 树附加至该元素上,但与其自身真正的子项分离开来。这一作用域子树称为影子树。被附着的元素称为影子宿主。 您在影子中添加的任何项均将成为宿主元素的本地项,包括 <style>。 这就是 shadow DOM 实现 CSS 样式作用域的方式。
创建 shadow DOM (element.attachShadow())
const header = document.createElement('header');
const shadowRoot = header.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>'; // Could also use appendChild().
// header.shadowRoot === shadowRoot
// shadowRoot.host === header
规范定义了一些元素无法托管影子树,原因有2点:
浏览器已为该元素托管其自身的内部 shadow DOM(<textarea>、<input>)。
让元素托管 shadow DOM 毫无意义 (<img>)。
为自定义元素创建 shadow DOM
// Use custom elements API v1 to register a new HTML tag and define its JS behavior
// using an ES6 class. Every instance of <fancy-tab> will have this same prototype.
customElements.define('fancy-tabs', class extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
// Attach a shadow root to <fancy-tabs>.
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>#tabs { ... }</style> <!-- styles are scoped to fancy-tabs! -->
<div id="tabs">...</div>
<div id="panels">...</div>
`;
}
...
});
从前端组件化到Web Components
在 Github 等社区和绝大多数互联网公司,从未停止过对前端组件化的尝试。 这些尝试产生了大量的各式各样的组件化技术,它们在解决的问题粒度、 提倡的架构设计、编译和处理的时机等方面各有不同:
为什么前端组件化?
前端组件化困难的一些原因
现有的组件化的一些方案?
什么是Web Components?
Web Components 是 WHATWG 和 W3C 正在尝试的 Web 组件化方案,为组件化的 Web 前端开发提供浏览器级别的支持。最早的努力当数 “Web Component” 规范说明 circa 2011 的出现,并在同年的 Fronteers Conference 大会上由 Alex Russell 将之宣之于众。该 Web Component 规范的产生和发展,旨在提供一种权威的、浏览器能理解的方式来创建组件。
Web Components 是一系列 Web 技术的集合,主要包括 Shadow DOM、Custom Elements、HTML templates、HTML Import(废弃,HTML Modules替换)。
主要的技术与兼容性
Custom elements:可重用网络组件。
HTML 现行标准缺乏自动关联 JS 行为和标记的方法。自定义元素使 HTML 变得现代化;补充了缺少的部件,并将结构与行为相结合。 如果 HTML 无法为问题提供解决方案,我们可以创建自定义元素来解决。 自定义元素在保留 HTML 优点的同时为浏览器带来新功能。
// Or use an anonymous class if you don't want a named constructor in current scope. window.customElements.define('app-drawer', class extends HTMLElement {...});
拓展自定义元素
customElements.define('fancy-app-drawer', FancyDrawer);
扩展原生元素时,对 define() 的调用会稍有不同。所需的第三个参数告知浏览器要扩展的标记。这很有必要,因为许多 HTML 标记均使用同一 DOM 接口。例如,<section>、<address> 和 <em>(以及其他)都使用 HTMLElement;<q> 和 <blockquote> 则使用 HTMLQuoteElement;等等。指定 {extends: 'blockquote'} 可让浏览器知道您创建的是增强的 <blockquote> 而不是 <q>。有关 HTML DOM 接口的完整列表,请参阅 HTML 规范。
自定义元素的响应 自定义元素可以定义特殊生命周期钩子,以便在其存续的特定时间内运行代码。 这称为自定义元素响应。
总结:
自定义元素提供了一种新工具,可让我们在浏览器中定义新 HTML 标记并创建可重用的组件。 将它们与 Shadow DOM 和 等新平台原语结合使用,我们可开始实现网络组件的宏大图景:
Shadow DOM — 对标签和样式的一层 DOM 包装
Shadow DOM 解决了构建网络应用的脆弱性问题。脆弱性是由 HTML、CSS 和 JS 的全局性引起的。 Shadow DOM 修复了 CSS 和 DOM。它在网络平台中引入作用域样式。 无需工具或命名约定,您即可使用原生 JavaScript 捆绑 CSS 和标记、隐藏实现详情以及编写独立的组件。
Shadow DOM 这款工具旨在构建基于组件的应用。因此,可为网络开发中的常见问题提供解决方案:
Shadow DOM 与普通 DOM 相同,但有两点区别: 1) 创建/使用的方式; 2) 与页面其他部分有关的行为方式。 通常,您创建 DOM 节点并将其附加至其他元素作为子项。 借助于 shadow DOM,您可以创建作用域 DOM 树,该 DOM 树附加至该元素上,但与其自身真正的子项分离开来。这一作用域子树称为影子树。被附着的元素称为影子宿主。 您在影子中添加的任何项均将成为宿主元素的本地项,包括 <style>。 这就是 shadow DOM 实现 CSS 样式作用域的方式。
创建 shadow DOM (element.attachShadow())
规范定义了一些元素无法托管影子树,原因有2点:
这里有几个有趣的事情。首先, 实例创建后,自定义元素创建其自身的 shadow DOM。这在 constructor() 中完成。其次,因为我们要创建一个影子根,因此