初识 web components

251 阅读5分钟

一:Web Component 的作用

Web Component 是一套不同的技术,允许你创建可重用的定制元素,并且在你的 web 应用中使用它们。

可以用来做跨技术的组件库。

二:Web Component 的组成部分

它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突;

  • Custom element(自定义元素):一组 JavaScript API,允许您定义 custom elements 及其行为,然后可以在您的用户界面中按照需要使用它们。
  • Shadow DOM(影子 DOM):一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
  • HTML template(HTML 模板): <template><slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

使用 custom elements

customElements.define("word-count", WordCount, { extends: "p" });

CustomElementRegistry.define() 方法用来注册一个 custom element;

  • custom element 的名称不能是单个单词,且其中必须要有短横线
  • 用于定义元素行为的
  • 可选参数,一个包含 extends 属性的配置对象,是可选参数。它指定了所创建的元素继承自哪个内置元素,可以继承任何内置元素。

共有两种 custom elements:

  • Autonomous custom elements 是独立的元素,它不继承其他内建的 HTML 元素。

    • 创建:CustomElementRegistry.define() 第三个参数是不传的;
    • 使用:你可以直接把它们写成 HTML 标签的形式,来在页面上使用。例如 <popup-info>,或者是document.createElement("popup-info")这样。
  • Customized built-in elements 继承自基本的 HTML 元素。

    • 创建:在创建时,你必须指定所需扩展的元素(正如上面例子所示),
    • 使用:需要先写出基本的元素标签,并通过 is 属性指定 custom element 的名称。例如<p is="word-count">, 或者 document.createElement("p", { is: "word-count" })

使用 shadow DOM

Shadow DOM 允许将隐藏的 DOM 树附加到常规的 DOM 树中——它以 shadow root 节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的 DOM 元素一样。

这里,有一些 Shadow DOM 特有的术语需要我们了解:

  • Shadow host:一个常规 DOM 节点,Shadow DOM 会被附加到这个节点上。
  • Shadow tree:Shadow DOM 内部的 DOM 树。
  • Shadow boundary:Shadow DOM 结束的地方,也是常规 DOM 开始的地方。
  • Shadow root: Shadow tree 的根节点。

Shadow DOM 内部的元素始终不会影响到它外部的元素,这为封装提供了便利。比如 <video> 元素;

基本用法

可以使用 Element.attachShadow() 方法来将一个 shadow root 附加到任何一个元素上。它接受一个配置对象作为参数,该对象有一个 mode 属性,值可以是 open 或者 closed

let shadow = elementRef.attachShadow({ mode: "open" });
let shadow = elementRef.attachShadow({ mode: "closed" });

open 表示可以通过页面内的 JavaScript 方法来获取 Shadow DOM,例如使用 Element.shadowRoot 属性:

将 Shadow DOM 附加到一个元素之后,就可以使用 DOM APIs 对它进行操作,就和处理常规 DOM 一样;

使用 templates and slots

templates 基础用法

<template id="my-paragraph">
  <style>
    p {
      color: white;
      background-color: #666;
      padding: 5px;
    }
  </style>
  <p>My paragraph</p>
</template>
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" });
      shadowRoot.appendChild(templateContent.cloneNode(true));
    }
  },
);

使用 slot 增加灵活度

比如上面的例子,我们会将模板的 p 标签改成下面这样;

<p><slot name="my-text">My default text</slot></p>
<my-paragraph>
  <span slot="my-text">Let's have some different text!</span>
</my-paragraph>

三:生命周期

  • connectedCallback:当自定义元素第一次被连接到文档 DOM 时被调用(类似组件Mounted)。

  • disconnectedCallback:当自定义元素与文档 DOM 断开连接时被调用(类似Unmount)。

  • adoptedCallback:当自定义元素被移动到新文档时被调用。

  • attributeChangedCallback:当自定义元素的一个属性被增加、移除或更改时被调用。

    • 每当元素的属性变化时,attributeChangedCallback()回调函数会执行(属性的名称、旧值与新值)
    • 需要搭配 observedAttributes() get 函数,返回一个数组,包含了需要监听的属性名称;
    • static get observedAttributes() {
          return ['w', 'l'];
      }
      
      attributeChangedCallback(name, oldValue, newValue) {
        console.log('Custom square element attributes changed.');
      }
      

四:实现计数器

demo

五:开源实现

Lit

JS 版本 计数器

TS 版本 计数器

六:使用实践

React 与 Web Component 结合

Vue 与 Web Component 结合

七:浏览器兼容性

caniuse.com/?search=web…

对于不兼容的浏览器可以使用polyfills: webcomponents / polyfills

八:优势&劣势

优势:

  • 原生支持,不依赖于特定的框架或库。这意味着您可以在不同的项目中重复使用它们,而不必担心与其他代码的冲突。
  • 标准,只有HTML,CSS,JavaScript,不需要额外学习一些框架的特定语言。
  • Shadow DOM 技术允许开发人员隔离多个组件之间的样式和行为,避免了全局的 CSS 污染问题。

劣势:

  • Web Components 和其他原生元素一样,偏向于 UI 层面,与现在的主流前端框架的数据驱动不符,和现在的组件库能力上相比功能会比较弱。
  • 兼容性有待提升。
  • 如果不借助框架开发的话,写法会返璞归真,HTML CSS JS 会糅合在一个文件,html CSS 都是字符串的形式 ,没有高亮,格式也需要自己调整,对于开发人员来说还是难受的。