乐趣区

关于前端:Web-Components

什么是 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…

退出移动版