什么是 Web Components?就是相似 Vue/React 组件,但浏览器间接反对的组件。
它由三种次要技术组成,能够一起应用来创立具备封装性能的通用自定义元素,能够在任何中央重复使用,而无需放心代码抵触。
- Custom Elements:一组 JavaScript API,容许定义自定义元素及其行为,而后能够页面中依据须要应用它们。
- Shadow DOM:一组 JavaScript API,用于将封装的 ” 影子 ”DOM 树附加到元素(与主文档 DOM 离开出现)并管制相干性能。通过这种形式,你能够将元素的个性窃密,这样它们就能够编写脚本和设置款式,而不用放心与文档的其余局部发生冲突。
- HTML templates:<template> 和 <slot> 元素使你可能编写未在出现页面中显示的标记模板。而后能够多次重复应用这些作为自定义元素构造的根底。
一、实现一个 Web 组件
实现一个 Web 组件,只需实现以下步骤即可:
1、创立一个类,能够在其定义自定义组件的性能。
2、应用 CustomElementRegistry.define() 注册自定义组件元素,向其传递要定义的元素名称、指定其性能的类或函数,以及可选的继承自哪个元素。
3、如果须要,应用 Element.attachShadow() 办法将影子 DOM 附加到自定义元素。应用惯例 DOM 办法将子元素、事件侦听器等增加到 shadow DOM。
4、如果须要,应用 <template> 和 <slot> 定义 HTML 模板。再次应用惯例 DOM 办法克隆模板并将其附加到您的影子 DOM。
5、在页面上的任意地位应用自定义元素,就像应用任何惯例 HTML 元素一样。
二、应用自定义组件
Web 文档上自定义元素的控制器是 CustomElementRegistry 对象——这个对象容许你在页面上注册一个自定义元素,返回对于注册了哪些自定义元素的信息等。你须要理解以下 3 个内容:
- 注册 -CustomElementRegistry.define()
- 生命周期
- 两种类型的自定义元素
(一)注册 -CustomElementRegistry.define()
有 3 个参数:
- 一个 DOMString 示意元素名称 。请留神,自定义元素名称须要在其中应用破折号,它们不能是单个词。
- 定义元素行为的类对象 。
- 可选,一个蕴含 extends 属性的选项对象 ,该属性指定你的元素继承自的内置元素(如果有)(仅与自定义内置元素相干)。
例如:这个元素叫做 word-count,它的类对象是 WordCount,它扩大了 <p> 元素。
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 元素来在页面上应用它们。例如 <popup-info> 或 document.createElement(“popup-info”)。 - Customized built-in elements
继承自根本的 HTML 元素。要创立其中之一,你必须指定它们扩大的元素,并且通过设置根本元素的 is 属性指定自定义元素的名称来应用它们。例如 <p is=”word-count”> 或 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"});
应用内置自定义组件,还有点不同:照常应用 <ul> 元素,但在 is 属性中需指定自定义元素的名称。
<ul is="expanding-list">
...
</ul>
三、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 为单个节点设置款式,或在 <style> 内为整个影子 DOM 树增加款式元素。不同之处在于 shadow DOM 外部的任何代码都不会影响其内部的任何内容 ,从而能够不便地进行封装。
请留神,shadow DOM 无论如何都不是什么陈腐事物——浏览器曾经应用它来封装元素的内部结构很长时间了。以一个 <video> 元素为例,它裸露了默认的浏览器控件。你在 DOM 中看到的只是 <video> 元素,但它的 shadow DOM 中蕴含一系列按钮和其余控件。shadow DOM 标准曾经容许你实际操作你本人的自定义元素的 shadow DOM。
(一)shadow DOM 用法
能够应用 Element.attachShadow() 办法将 shadow root 附加到任何元素。它将蕴含一个 option(mode)的选项对象作为其参数,其值为 open 或 closed:
let shadow = elementRef.attachShadow({mode: 'open'});
let shadow = elementRef.attachShadow({mode: 'closed'});
open 意味着你能够应用在主页面上下文中编写的 JavaScript 拜访 shadow DOM,例如应用 Element.shadowRoot 属性:
let myShadowDom = myCustomElem.shadowRoot;
当须要将 shadow DOM 附加到元素时,操作它只需应用与惯例 DOM 操作雷同的 DOM API:
let para = document.createElement('p');
shadow.appendChild(para);
// etc.
示例:
// HTML
<label for="cvc">Enter your CVC <popup-info img="img/alt.png" data-text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."></popup-info></label>
<input type="text" id="cvc">
// JS
// Create a class for the element
class PopUpInfo extends HTMLElement {constructor() {
// Always call super first in constructor
super();
// Create a shadow root
const shadow = this.attachShadow({mode: 'open'});
// Create spans
const wrapper = document.createElement('span');
wrapper.setAttribute('class', 'wrapper');
const icon = document.createElement('span');
icon.setAttribute('class', 'icon');
icon.setAttribute('tabindex', 0);
const info = document.createElement('span');
info.setAttribute('class', 'info');
// Take attribute content and put it inside the info span
const text = this.getAttribute('data-text');
info.textContent = text;
// Insert icon
let imgUrl;
if(this.hasAttribute('img')) {imgUrl = this.getAttribute('img');
} else {imgUrl = 'img/default.png';}
const img = document.createElement('img');
img.src = imgUrl;
icon.appendChild(img);
// 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);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);
}
}
// Define the new element
customElements.define('popup-info', PopUpInfo);
显示后果:
(二)Using templates and slots
应用 <template> 和 <slot> 元素创立灵便的模板,而后应用该模板来填充 Web 组件的 shadow DOM。
例如:
<template id="my-paragraph">
<p>My paragraph</p>
</template>
这不会呈现在你的页面中,除非你应用 JavaScript 获取它的内容,而后将内容附加到 DOM 中:
let template = document.getElementById('my-paragraph');
let templateContent = template.content;
document.body.appendChild(templateContent);
(三)Using templates with web components
模板自身很有用,但它们与 Web 组件一起工作得更好。让咱们定义一个 web 组件,它应用咱们的模板作为它的 shadow DOM 的内容。咱们将其称为 <my-paragraph>:
customElements.define('my-paragraph',
class extends HTMLElement {constructor() {super();
let template = document.getElementById('my-paragraph');
let templateContent = template.content;
const shadowRoot = this.attachShadow({mode: 'open'})
.appendChild(templateContent.cloneNode(true));
}
}
);
<template id="my-paragraph">
<style>
p {
color: white;
background-color: #666;
padding: 5px;
}
</style>
<p>My paragraph</p>
</template>
在 HTML 中间接应用:
<my-paragraph></my-paragraph>
这里要留神的关键点是咱们将模板内容的克隆附加到 shadow root,应用 Node.cloneNode() 办法创立。
因为咱们将其内容附加到 shadow DOM,咱们能够在模板内的 <style> 元素中蕴含一些款式信息,而后将其封装在自定义元素中。如果咱们只是将它附加到规范 DOM,这将不起作用。
(四)Adding flexibility with slots
Slots 由它们的 name 属性标识,并容许你在模板中定义占位符,当在标记中应用元素时,能够用你想要的任何标记片段填充该占位符。
因而,如果咱们想在咱们的简略示例中增加一个插槽,咱们能够像这样更新模板的段落元素:
<p><slot name="my-text">My default text</slot></p>
如果在标记中蕴含元素时未定义插槽的内容,或者浏览器不反对插槽,则 <my-paragraph> 仅蕴含后备内容“My default text”。
为了定义槽的内容,咱们在 <my-paragraph> 元素中蕴含了一个 HTML 构造,它的槽属性的值等于咱们心愿它填充的槽的名称。和以前一样,这能够是你喜爱的任何内容,例如:
<my-paragraph>
<span slot="my-text">Let's have some different text!</span>
</my-paragraph>
<my-paragraph>
<ul slot="my-text">
<li>Let's have some different text!</li>
<li>In a list!</li>
</ul>
</my-paragraph>
欢送关注我的集体公众号:
参考链接:
https://developer.mozilla.org…