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

Web Component -- 即将爆发的原生的 UI 组件化标准_第1张图片

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 Compnent IE兼容性仅11可通过Polyfill支持

TL;DR

Web Component -- 即将爆发的原生的 UI 组件化标准_第2张图片 实例:用异构系统共建 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:

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

class FlagIcon extends HTMLElement {
  constructor() {
    super();
    this._countryCode = null;
  }

  static observedAttributes = ["country"];

  attributeChangedCallback(name, oldValue, newValue) {
    // name will always be "country" due to observedAttributes
    this._countryCode = newValue;
    this._updateRendering();
  }
  connectedCallback() {
    this._updateRendering();
  }

  get country() {
    return this._countryCode;
  }
  set country(v) {
    this.setAttribute("country", v);
  }

  _updateRendering() {
    ...
  }
}

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

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

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

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

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

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

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

// Create a class for the element
class WordCount extends HTMLParagraphElement {
  constructor() {
    // Always call super first in constructor
    super();

    // Constructor contents omitted for brevity
    // …
  }
}

// Define the new element
customElements.define("word-count", WordCount, { extends: "p" });

Shadow DOM

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

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

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

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