什么是Web Components?就是类似Vue/React组件,但浏览器直接支持的组件。
它由三种主要技术组成,可以一起使用来创建具有封装功能的通用自定义元素,可以在任何地方重复使用,而无需担心代码冲突。
- Custom Elements:一组 JavaScript API,允许定义自定义元素及其行为,然后可以页面中根据需要使用它们。
- Shadow DOM:一组 JavaScript API,用于将封装的"影子"DOM 树附加到元素(与主文档 DOM 分开呈现)并控制相关功能。通过这种方式,你可以将元素的特性保密,这样它们就可以编写脚本和设置样式,而不必担心与文档的其他部分发生冲突。
- HTML templates:和
元素使你能够编写未在呈现页面中显示的标记模板。然后可以多次重复使用这些作为自定义元素结构的基础。
一、实现一个Web组件
实现一个Web组件,只需完成以下步骤即可:
1、创建一个类,可以在其定义自定义组件的功能。
2、使用CustomElementRegistry.define()注册自定义组件元素,向其传递要定义的元素名称、指定其功能的类或函数,以及可选的继承自哪个元素。
3、如果需要,使用 Element.attachShadow() 方法将影子 DOM 附加到自定义元素。使用常规 DOM 方法将子元素、事件侦听器等添加到 shadow DOM。
4、如果需要,使用 和
5、在页面上的任意位置使用自定义元素,就像使用任何常规 HTML 元素一样。
二、使用自定义组件
Web 文档上自定义元素的控制器是 CustomElementRegistry 对象——这个对象允许你在页面上注册一个自定义元素,返回关于注册了哪些自定义元素的信息等。你需要了解以下3个内容:
- 注册-CustomElementRegistry.define()
- 生命周期
- 两种类型的自定义元素
(一)注册-CustomElementRegistry.define()
有3个参数:
- 一个 DOMString 表示元素名称。请注意,自定义元素名称需要在其中使用破折号,它们不能是单个词。
- 定义元素行为的类对象。
- 可选,一个包含 extends 属性的选项对象,该属性指定你的元素继承自的内置元素(如果有)(仅与自定义内置元素相关)。
例如:这个元素叫做 word-count,它的类对象是 WordCount,它扩展了
元素。
customElements.define('word-count', WordCount, { extends: 'p' });
WordCount结构如下:
class WordCount extends HTMLParagraphElement {
constructor() {
// Always call super first in constructor
super();
// Element functionality written in here
...
}
}
(二)生命周期
可以在自定义元素的类定义中定义多个不同的回调,它们在元素生命周期的不同点触发:
- connectedCallback:每次将自定义元素附加到文档连接元素时调用。每次移动节点时都会发生这种情况,并且可能在元素的内容完全解析之前发生。
注意:一旦元素不再连接,可能会调用 connectedCallback,请使用 Node.isConnected 来确保。 - disconnectedCallback:每次自定义元素与文档的 DOM 断开连接时调用。
- adoptedCallback:每次将自定义元素移动到新文档时调用。
attributeChangedCallback:每次添加、删除或更改自定义元素的属性之一时调用。在静态调用observedAttributes方法中指定要注意更改的属性。
注意:要在属性更改时触发 attributeChangedCallback() 回调,必须观察这些属性。这是通过在自定义元素类中指定一个静态的 get observeAttributes() 方法来完成的 - 这应该返回一个包含你要观察的属性名称的数组:static get observedAttributes() { return ['c', 'l']; }
(三)两种类型的自定义元素
- Autonomous custom elements
独立的——它们不继承自标准的 HTML 元素。你可以通过将它们字面上写为 HTML 元素来在页面上使用它们。例如或 document.createElement("popup-info")。 - Customized built-in elements
继承自基本的 HTML 元素。要创建其中之一,你必须指定它们扩展的元素,并且通过设置基本元素的 is 属性指定自定义元素的名称来使用它们。例如或 document.createElement("p", { is: "word-count" })。
1、Autonomous custom elements
class PopUpInfo extends HTMLElement {
constructor() {
// Always call super first in constructor
super();
// write element functionality in here
...
}
}
最后,注册自定义组件:
customElements.define('popup-info', PopUpInfo);
如果你想引入外部样式文件的方式引入样式:
// Apply external styles to the shadow dom
const linkElem = document.createElement('link');
linkElem.setAttribute('rel', 'stylesheet');
linkElem.setAttribute('href', 'style.css');
// Attach the created element to the shadow dom
shadow.appendChild(linkElem);
2、Customized built-in elements
class ExpandingList extends HTMLUListElement {
constructor() {
// Always call super first in constructor
super();
// write element functionality in here
...
}
}
最后,注册自定义组件:
customElements.define('expanding-list', ExpandingList, { extends: "ul" });
使用内置自定义组件,还有点不同:照常使用
- 元素,但在 is 属性中需指定自定义元素的名称。
...
三、Using shadow DOM
Web 组件的一个重要方面是封装——能够将标记结构、样式和行为隐藏起来并与页面上的其他代码分开,这样不同的部分就不会发生冲突,并且代码可以保持整洁。Shadow DOM API 是其中的关键部分,它提供了一种将隐藏的分离 DOM 附加到元素的方法。
Shadow DOM 允许隐藏的 DOM 树附加到常规 DOM 树中的元素——这个 shadow DOM 树从一个影子根开始,在它下面可以附加到任何你想要的元素,就像普通 DOM 一样。
- Shadow host:Shadow DOM 附加到的常规 DOM 节点。
- Shadow tree:Shadow DOM 内部的 DOM 树。
- Shadow boundary:Shadow DOM 结束的地方,普通 DOM 开始的地方。
- Shadow root:Shadow tree的根节点。
你可以以与非影子节点完全相同的方式影响影子 DOM 中的节点——例如,附加子项或设置属性、使用 element.style.foo 为单个节点设置样式,或在
My paragraph
在HTML中直接使用:
这里要注意的关键点是我们将模板内容的克隆附加到shadow root,使用 Node.cloneNode() 方法创建。
因为我们将其内容附加到 shadow DOM,我们可以在模板内的