Web Component-初识

2022-04-20 21:16:562022-01-08 22:40:37

Web Component 有点东西

什么是 Web Component

Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 web 应用中使用它们。--MDN

简单来讲就是浏览器提供的原生组件复用方案。主要由三种技术方案实现:

  • Custom elements(自定义元素)。可以用来定义标签(元素)

  • Shadow DOM(影子 DOM)。可以用来做样式隔离

  • HTML templates(HTML 模板)。可以用来定义一个基础的组件的dom结构

怎么用 Web Component

实现 Web Component 的最基本流程:

  1. 使用template来定义一个组件的dom
<template id="mHeader">
  <div>
    <span class="content" id="content">我是头部</span>
  </div>
</template>
  1. 接着基于👆创建的template创建一个类组件
class MHeader extends HTMLElement {
  constructor() {
    super();
    // attachShadow() 方法来将一个 shadow root 附加到任何一个元素上,该方法返回一个 shadow root 。
    const shadowRoot = this.attachShadow({ mode: "closed" });
    const template = document.querySelector("#mHeader");
    const content = template.content.cloneNode(true);
    shadowRoot.appendChild(content);
  }
}
  1. CustomElementRegistry.define() 方法注册自定义标签(元素)
/**
 * 注意,组件名是有限制的,MHeader、header1等都是不能用的,浏览器会报错
 * 
 * web-component.html:33 Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': "header1" is not a valid custom element name
    at file:///Users/echo/Desktop/web-component.html:33:27
 */
window.customElements.define("m-header", MHeader);
  1. 使用该组件
<!-- 使用的地方没有限制,自闭合也是可以的 -->
<m-header />
  1. 给组件加点样式

可以直接在template里添加style标签,在里面可以添加只在该template下生效的样式

:host选择器可以选择组件的根元素

<template id="mHeader">
  <style>
    :host {
      font-size: 32px;
    }
    .content {
      color: #f00;
    }
  </style>
  <div>
    <span class="content" id="content">我是头部</span>
  </div>
</template>
  1. 加点属性试试 🤔

需要在第 2 步在定义组件时用到this.getAttribute来获取传入的属性

class MHeader extends HTMLElement {
  constructor() {
    // ...
    const title = this.getAttribute("title");
    if (title) {
      content.querySelector("#content").innerText = title;
    }
    // ...
  }
}

<m-header title="hello" />;
  1. 添加一些交互

可以使用querySelector获取到template内部的一些元素来添加事件

<template id="mHeader">
  <div>
    <!-- ... -->
    <button id="login">login</button>
  </div>
</template>

接着绑定事件

class MHeader extends HTMLElement {
  constructor() {
    // ...
    content.querySelector("#login").addEventListener("click", () => {
      alert("login");
    });
    // ...
  }
}
  1. 与父组件的通信

通过CustomEvent来进行通信

完整代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <template id="mHeader">
      <style>
        :host {
          font-size: 32px;
        }

        .content {
          color: #f00;
        }
      </style>
      <div>
        <span class="content" id="content">我是头部</span>
        <button id="login">login</button>
      </div>
    </template>

    <m-header id="mHeader" title="hello" />

    <script>
      class MHeader extends HTMLElement {
        constructor() {
          super();
          const shadowRoot = this.attachShadow({ mode: "closed" });
          const template = document.querySelector("#mHeader");
          const content = template.content.cloneNode(true);
          const title = this.getAttribute("title");
          if (title) {
            content.querySelector("#content").innerText = title;
          }
          const myEvent = new CustomEvent("login", {
            detail: "这是子组件传过来的消息",
          });
          const loginBtn = content.querySelector("#login");
          loginBtn.addEventListener("click", () => {
            alert("login");
          });
          shadowRoot.appendChild(content);
        }
      }

      window.customElements.define("m-header", MHeader);
    </script>
  </body>
</html>

未完待续

to be continued...

基于 Web Component 的落地应用

GitHub。GitHub是基于 Web Components 来开发的

image.png

其次,Vue.js和微信小程序也是基于 Web Components 来做组件化的

总结

Web Component 是浏览器提供的可以用于组件复用的方案,可以实现样式隔离,自定义属性、父子组件通信等功能,有不少应用/框架都是基于Web Component来进行开发的,前景广阔。但Web Component书写起来还是有些不够便捷,需要用到原生 dom 操作 api

参考

MDN-Web Components

MDN/web-components-examples