web components 在前端技术栈中的使用方式总结

30 阅读2分钟

📦 Web Components 系统化语法与用例手册

一、概述

Web Components 是一组浏览器原生的 API 规范,支持创建可重用、封装良好的 UI 组件,而无需依赖框架。

Web Components 包括 3 个核心规范:

  1. Custom Elements – 自定义 HTML 元素的声明与生命周期
  2. Shadow DOM – 样式/结构封装的 DOM 子树
  3. HTML Templates – 可复用的模板结构

兼容性良好,现代浏览器均已原生支持。


二、Custom Elements 自定义元素

✨ 语法结构

class MyElement extends HTMLElement {
  // 生命周期回调
  connectedCallback() {}
  disconnectedCallback() {}
  adoptedCallback() {}
  attributeChangedCallback(name, oldVal, newVal) {}

  // 属性监听列表
  static get observedAttributes() {
    return ['title', 'active']
  }

  // DOM property getter/setter
  get title() {}
  set title(value) {}
}

// 注册元素名(必须含有中划线)
customElements.define('my-element', MyElement)

✅ 用例示例

<my-element title="Hello" active></my-element>
const el = document.querySelector('my-element')
el.title = 'World'           // 设置 DOM property
el.setAttribute('title', 'X') // 设置 HTML attribute(会触发反射)

三、Shadow DOM 封装

✨ 语法结构

class MyShadowEl extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: 'open' }) // or "closed"
    this.shadowRoot.innerHTML = `
      <style>
        :host([active]) { color: red; }
      </style>
      <slot></slot>
    `
  }
}
customElements.define('my-shadow', MyShadowEl)

✅ 用例示例

<my-shadow active>Hello</my-shadow>

特点:

  • 内部样式隔离
  • 外部不可选中内部元素
  • :host 表示宿主元素本身

四、HTML Templates

✨ 语法结构

<template id="card-template">
  <style>
    .card { border: 1px solid #ccc; }
  </style>
  <div class="card">
    <slot name="content"></slot>
  </div>
</template>

✅ 用例示例

class CardBox extends HTMLElement {
  constructor() {
    super()
    const template = document.getElementById('card-template')
    const clone = template.content.cloneNode(true)
    this.attachShadow({ mode: 'open' }).appendChild(clone)
  }
}
customElements.define('card-box', CardBox)
<card-box>
  <div slot="content">Hello in a Card!</div>
</card-box>

五、Property ↔ Attribute 映射与反射

🌐 映射关系机制

类型HTML AttributeDOM Property
字符串<el title="abc">el.title = 'abc'
布尔值<el active> / not existel.active = true/false
对象/数组❌ 无法表示✅ 可通过 property

🧩 手动反射机制(推荐)

get active() {
  return this.hasAttribute('active')
}
set active(val) {
  if (val) {
    this.setAttribute('active', '')
  } else {
    this.removeAttribute('active')
  }
}

否则,框架(如 Vue/React)无法正确传递状态。


六、生命周期钩子(custom element callbacks)

方法名触发时机
constructor()实例化时
connectedCallback()插入 DOM 时
disconnectedCallback()移出 DOM 时
adoptedCallback()被移动到新文档时
attributeChangedCallback(name, oldVal, newVal)监听的属性变化时

七、事件派发 & 双向通信

✨ 组件内派发事件

this.dispatchEvent(new CustomEvent('custom-click', {
  detail: { x: 1 },
  bubbles: true,
  composed: true,
}))

✅ 外部监听事件

<my-element></my-element>

<script>
document.querySelector('my-element').addEventListener('custom-click', e => {
  console.log(e.detail)
})
</script>

八、框架集成注意事项(以 Vue 为例)

框架绑定值结果说明
:title="msg"设置为 attribute,除非 prop 存在Vue 检查 prop in el 决定绑定方式
:title.prop="msg"强制设置为 DOM property用于传递对象、布尔值、函数等复杂值

✅ 推荐做法(以布尔值为例)

<my-toggle :checked.prop="true" />

否则:

<my-toggle checked="false" /> <!-- 实际为 true -->

九、最佳实践建议

  • 使用 observedAttributes + attributeChangedCallback 响应外部变更
  • 为每个属性写明 getter/setter,并处理 reflect
  • 使用 slot<template> 提高可插拔性
  • 样式封装时配合 :host, ::slotted, :host-context
  • CustomEvent + dispatchEvent 实现向上传递

十、结语

Web Components 为组件封装提供了浏览器级标准方案,其核心优势是:

  • 原生支持,无框架依赖
  • 可与任意框架集成(Vue、React、Svelte)
  • 在设计系统、微前端、SDK 嵌入场景中具备长期生命力

在现代应用中,可以将 Web Components 视为“技术内核”或“平台桥梁”,作为可复用 UI 模块的标准载体。