• Web Component-初识


    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
    • 2
    • 3
    • 4
    • 5
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 使用该_组件_
    <!-- 使用的地方没有限制,自闭合也是可以的 -->
    <m-header /> 
    
    • 1
    • 2
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 添加一些交互

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

    <template id="mHeader">
      <div>
        <!-- ... -->
        <button id="login">login</button>
      </div>
    </template> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    接着绑定事件

    class MHeader extends HTMLElement {
      constructor() {
        // ...
        content.querySelector("#login").addEventListener("click", () => {
          alert("login");
        });
        // ...
      }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    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> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    未完待续

    to be continued…

    基于 Web Component 的落地应用

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

    image.png

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

    总结

    Web Component 是浏览器提供的可以用于组件复用的方案,可以实现样式隔离,自定义属性、父子组件通信等功能,但书写起来有些不够便捷,需要用到原生 dom 操作 api

    参考

    MDN-Web Components

    MDN/web-components-examples

  • 相关阅读:
    2023.9.8 基于传输层协议 UDP 和 TCP 编写网络通信程序
    【解决方案】Java 互联网项目中消息通知系统的设计与实现(上)
    运算符之算术运算符、关系运算符、逻辑运算符、复合赋值运算符、其他运算符
    新160个CrackMe分析-第6组:51-60(下)
    优惠来袭,工业树莓派特惠季火爆进行中
    《动态规划 ---- 线性规划一》----- 动态规划的基本概念,线性动态规划-->背包问题
    6、SpringBoot_项目的打包与运行
    【Unity UI ToolKit】01 - 新手教程 从控件创建到C#代码控制
    算法学习十八补二叉树递归套路+贪心算法一
    第一章概述
  • 原文地址:https://blog.csdn.net/web2022050901/article/details/125530377