• Web Component -- 即将爆发的原生的 UI 组件化标准


    c05c703df9c23a75070b3c3dbb44a55c.png

    Web Component 概述

    Web Component 是一种用于构建可复用用户界面组件的技术,开发者可以创建自定义的 HTML 标签,并将其封装为包含逻辑和样式的独立组件,从而在任何 Web 应用中重复使用。

    每个 Web Component 都具有自己的 DOM 和样式隔离,避免了全局 CSS 和 JavaScript 的冲突问题。它还支持自定义事件和属性,可以与其他组件进行通信和交互。

    不同于 Vue/React 等社区或厂商的组件化开发方案,Web Component 被定义在标准的 HTML 和 DOM 标准中。它由一组相关的 Web 平台 API 组成,也可以与现有的前端框架和库配合使用。

    Web Component 的兼容性良好,可以在现代浏览器中直接使用,也可以通过 polyfill 兼容到旧版浏览器(IE11 理论上可以兼容,出于初步调研的考虑,本文不对兼容性作过多探讨)

    同类组件化方案比较

    Pros技术Cons
    可以异构Micro Frontend需要主应用、对子应用有侵入、样式统一困难
    模块级的多项目在运行时共享Module Federation主要依赖webpack5,既有项目改造成本未知;实现异构引用需要借助其他插件
    模块级动态共享Vue :is + 动态import依赖vue技术栈
    可以异构、完全解耦、对原有开发方法改造极小Web CompnentIE兼容性仅11可通过Polyfill支持

    TL;DR

    56dcc19c97bc6ada91d4380d94f19759.jpeg 实例:用异构系统共建 web components
    https://gitee.com/tonylua/web-component-test1/tree/master

    Web Component 关键特性

    Custom Elements(自定义元素)

    是 Web 标准中的一项功能,它允许开发者自定义新的 HTML 元素,开发者可以使用 JavaScript 和 DOM API,使新元素具有自定义的行为和功能

    4.13.1.1 Creating an autonomous custom element

    This section is non-normative.

    For the purposes of illustrating how to create an autonomous custom element, let's define a custom element that encapsulates rendering a small icon for a country flag. Our goal is to be able to use it like so:

    "nl">

    To do this, we first declare a class for the custom element, extending HTMLElement:

    1. class FlagIcon extends HTMLElement {
    2.   constructor() {
    3.     super();
    4.     this._countryCode = null;
    5.   }
    6.   static observedAttributes = ["country"];
    7.   attributeChangedCallback(name, oldValue, newValue) {
    8.     // name will always be "country" due to observedAttributes
    9.     this._countryCode = newValue;
    10.     this._updateRendering();
    11.   }
    12.   connectedCallback() {
    13.     this._updateRendering();
    14.   }
    15.   get country() {
    16.     return this._countryCode;
    17.   }
    18.   set country(v) {
    19.     this.setAttribute("country", v);
    20.   }
    21.   _updateRendering() {
    22.     ...
    23.   }
    24. }

    We then need to use this class to define the element:

    customElements.define("flag-icon", FlagIcon);
    • 继承自基类 HTMLElement

    • 自定义的元素名称需符合 DOMString 标准,简单来说就是必须带短横线

    • 其中 observedAttributes 声明的属性才能被 attributeChangedCallback() 监听

    • 完整生命周期方法说明为:

    1. class MyCustomElement extends HTMLElement {
    2.   constructor() {
    3.     super();
    4.     // 在构造函数中进行初始化操作
    5.     // 用 this.appendChild(...) 等挂载到dom中
    6.     // 用 addEventListener() 绑定事件到 this.xxx 上
    7.   }
    8.   connectedCallback() {
    9.     // 元素被插入到文档时触发,等价于 vue 的 mounted
    10.   }
    11.   disconnectedCallback() {
    12.     // 元素从文档中移除时触发,等价于 vue 的 beforeDestory / destoyed
    13.   }
    14.   attributeChangedCallback(attributeName, oldValue, newValue) {
    15.     // 元素的属性被添加、移除或更改时触发,等价于 vue 的 beforeUpdate / updated
    16.   }
    17. }

    除了继承 HTMLElement,也可以继承其既有子类,并在使用是采用原生标签(被继承类) + is 语法,如:

    1. // Create a class for the element
    2. class WordCount extends HTMLParagraphElement {
    3.   constructor() {
    4.     // Always call super first in constructor
    5.     super();
    6.     // Constructor contents omitted for brevity
    7.     // …
    8.   }
    9. }
    10. // Define the new element
    11. customElements.define("word-count", WordCount, { extends: "p" });
    "word-count">

    Shadow DOM

    DOM 编程模型令人诟病的一个方面就是缺乏封装,不同组件之间的逻辑和样式很容易互相污染。

    鉴于这个原因,Web components 的一个重要属性就是封装——可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离。其中,Shadow DOM 接口是关键所在,它可以将一个隐藏的、独立的 DOM 附加到一个元素上

    Shadow DOM 是 DOM nodes 的附属树。这种 Shadow DOM 子树可以与某宿主元素相关联,但并不作为该元素的普通子节点,而是会形成其自有的作用域;Shadow DOM 中的根及其子节点也不可见。

    相比于以前为了实现封装而只能使用