Web Components 现代实践:标准化、可靠跨框架组件的完整指南
Web Components 被吹捧了 10+ 年,为什么到今天仍然"不那么流行"?
不是技术问题,而是心智问题:很多人以为 Web Components 是想替代 React/Vue,实际上它们是互补的。真实的场景是:
- 公司有多个框架的项目,想共享 UI 组件库
- 想写一个与框架无关的插件/扩展
- 产品要集成第三方贡献的 widget
在这些场景下,Web Components 就特别有用。
1. Web Components 的三大核心
Custom Elements
定义你自己的 HTML 标签,像这样:
class MyButton extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `<button>Click me</button>`
}
}
customElements.define('my-button', MyButton)
然后就能 <my-button></my-button> 直接用。
Shadow DOM
组件内部的 DOM 树与外部隔离,样式不会互相污染。这对大型应用特别有用。
HTML Templates
<template> 标签存储待复用的 HTML 片段,不会被立即解析和渲染。
2. 实战建议:从 Custom Elements 开始
不要贪心同时学三个概念。先专注 Custom Elements:
class Counter extends HTMLElement {
constructor() {
super()
this.count = 0
}
connectedCallback() {
this.render()
}
render() {
this.innerHTML = `
<p>Count: ${this.count}</p>
<button onclick="${this.increment.bind(this)}">+</button>
`
}
increment() {
this.count++
this.render()
}
}
customElements.define('my-counter', Counter)
这样写就能跨框架共享。
3. Shadow DOM 的两大价值
隔离样式
class StyledButton extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<style>
button { background: blue; } /* 只影响组件内部 */
</style>
<button><slot></slot></button>
`
}
}
组件内的 button { background: blue } 不会影响页面其他按钮。
隔离 DOM 结构
选择器无法穿透 Shadow DOM 边界,保护组件内部实现细节。
4. Props、Attributes 与 Slots
Attributes(HTML 属性)
<my-button size="large" disabled></my-button>
在组件中读取:
const size = this.getAttribute('size')
传递简单值用 attributes。
Properties(JS 属性)
element.data = { userId: 123 }
传递复杂对象用 properties。
Slots(内容分发)
<my-button>Click here</my-button>
组件模板中:
<button><slot></slot></button>
5. 事件通信
Web Components 推荐通过自定义事件通信:
class Counter extends HTMLElement {
increment() {
this.count++
this.dispatchEvent(
new CustomEvent('count-changed', {
detail: { count: this.count },
bubbles: true
})
)
}
}
// 使用
document.querySelector('my-counter').addEventListener(
'count-changed',
(e) => console.log('新 count:', e.detail.count)
)
好处是框架无关,任何地方都能监听。
6. 与现代框架的集成
虽然 Web Components 框架无关,但近年 React、Vue、Angular 都改进了支持。
Vue 中用 Web Components
<my-custom-element :prop="value" @custom-event="handler"></my-custom-element>
React 中用 Web Components
<MyCustomElement prop={value} onCustomEvent={handler} />
只要属性名和事件名约定好,就能无缝集成。
7. 实际痛点与解决方案
痛点 A:手写 querySelector 太繁琐
方案:用第三方库如 lit-element or fast-element,降低模板和状态管理成本。
痛点 B:样式怎么深度定制
方案:暴露 CSS Variables:
this.shadowRoot.innerHTML = `
<style>
button {
background: var(--button-bg, blue);
}
</style>
`
使用方可以 --button-bg: red 覆盖。
痛点 C:在线编辑器能用吗
方案:GrapesJS 等编辑器可以通过 iframe 或动态脚本加载 Web Components,但有限制。
8. 选择建议
适合用 Web Components:
- 多框架共享 UI 库
- 建设 Design System & 跨项目复用
- 第三方 widget 集成
- 基础设施类的独立功能
不适合:
- 单个 React/Vue 项目的内部组件(用原生框架更简洁)
- 需要很强的状态管理和开发者工具的复杂应用


