返回首页

Web Components:关于 Custom Elements 的神话与真相

本文揭穿 web components 的神话:属性传递、Shadow DOM、钩子。提供代码解决方案,并突出复杂仪表板和小部件的优势。

为开发者揭穿 web components 的神话
Advertisement 728x90

Web Components:破除误解,探索真实能力

Web组件常被批评为只能通过属性传递字符串。这一误解源于对attributeChangedCallback机制的误读。HTML属性是标记的静态部分,不直接链接到JS运行时。要传递对象、数组或原始值,请在组件类中使用getter/setter。浏览器允许通过DOM API或代理直接设置任何类型的属性。

class MyElement extends HTMLElement {
  set data(value) {
    this._data = value;
    this.render();
  }
}

Shadow DOM提供样式和DOM隔离,防止外部冲突。批评隔离为缺陷忽略了选择:自定义元素无需Shadow DOM即可工作。对于自定义,使用CSS变量和::part()

my-component::part(button) {
  background: var(--accent-color);
}

样式重复的误解:Adopted Style Sheets允许将共享样式应用于所有Shadow DOM实例。

Google AdInline article slot

生命周期始于构造函数,而非连接到DOM时。组件在内存中创建,并通过DocumentFragment操作以实现虚拟化而无需渲染。

半真半假:注册与生命周期钩子

全局自定义元素注册表要求唯一名称。解决方案是前缀(例如app-button)和注册时的try/catch:

try {
  customElements.define('app-button', ButtonElement);
} catch (e) {
  console.warn('名称冲突:', e);
}

冲突可通过测试捕获。扁平命名空间是JS、DNS和HTML中的标准模式。

Google AdInline article slot

connectedCallback/disconnectedCallback钩子在DOM中移动时触发。修复方法是初始化标志:

connectedCallback() {
  if (this._initialized) return;
  this._initialized = true;
  this.init();
}

注销不可能,但这是优点:浏览器缓存构造函数而无开销。

真实细节与解决方案

HTML解析在打开标签时触发connectedCallback,然后处理内容。DOM API不可用。使用defertype="module"进行延迟初始化。替代方案是customElements.whenDefined()

Google AdInline article slot
const button = document.querySelector('app-button');
await customElements.whenDefined('app-button');
button.method();

为保证访问子元素,使用renderCallback或类似钩子。

Web Components的优势

Web组件原生链接HTML标记与JS。自定义标签作为服务器或客户端集成的标记,无供应商锁定。

原子生命周期管理:浏览器独立于框架通知连接/断开。

Lit、Symbiote.js和微前端的基础模型。与React、Vue、Angular、SPA和SSR兼容。

  • 无开销隔离:Shadow DOM可选。
  • 原生响应性:属性 + 代理。
  • 多功能性:从部件到仪表板,支持WebSocket和图形。
  • 虚拟化:DocumentFragment用于10万+元素列表。
  • 样式:CSS变量、::part()、Adopted Style Sheets。

关键要点

  • 任何类型的属性通过getter/setter传递;属性仅用于字符串。
  • Shadow DOM隔离但可禁用;样式通过变量和::part()实现。
  • 全局注册表通过前缀和测试解决;冲突罕见。
  • connected/disconnected钩子带标志防止重复初始化。
  • 解析异步性通过defer/module或whenDefined修复。

— Editorial Team

Advertisement 728x90

继续阅读