Web Component 开发入门:创建可复用的自定义 HTML 元素
Web Component 允许你创建自定义的 HTML 元素,它们样式封装、行为独立、可在任何项目中使用——无论项目使用 React、Vue 还是原生 HTML。
核心技术
Web Component 由三项核心技术组成:
- Custom Elements — 定义新的 HTML 元素
- Shadow DOM — 样式和行为封装
- HTML Templates — 定义可复用的 HTML 结构
创建一个 Web Component
// 自定义按钮组件
class MyButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
static get observedAttributes() {
return ['variant', 'disabled'];
}
connectedCallback() {
this.render();
}
attributeChangedCallback() {
this.render();
}
render() {
const variant = this.getAttribute('variant') || 'primary';
const disabled = this.hasAttribute('disabled') ? 'disabled' : '';
this.shadowRoot.innerHTML = `
<style>
button {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
}
button.primary {
background: #4F46E5;
color: white;
}
button.primary:hover {
background: #4338CA;
}
button.outline {
background: transparent;
border: 2px solid #4F46E5;
color: #4F46E5;
}
button[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
</style>
<button class="${variant}" ${disabled}>
<slot></slot>
</button>
`;
// 事件处理
this.shadowRoot.querySelector('button')
.addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('my-click', {
detail: { id: this.id }
}));
});
}
}
// 注册自定义元素
customElements.define('my-button', MyButton);
使用自定义元素
<my-button variant="primary" id="submit-btn">提交</my-button>
<my-button variant="outline">取消</my-button>
<my-button disabled>已禁用</my-button>
<script>
document.querySelector('#submit-btn')
.addEventListener('my-click', (e) => {
console.log('Button clicked:', e.detail.id);
});
</script>
更复杂的例子:计数器组件
class Counter extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.count = 0;
}
connectedCallback() {
this.render();
this.shadowRoot.querySelector('#inc')
.addEventListener('click', () => this.update(1));
this.shadowRoot.querySelector('#dec')
.addEventListener('click', () => this.update(-1));
}
update(amount) {
this.count += amount;
this.shadowRoot.querySelector('#value').textContent = this.count;
this.dispatchEvent(new CustomEvent('count-change', {
detail: { count: this.count }
}));
}
render() {
this.shadowRoot.innerHTML = `
<style>
:host { display: inline-flex; align-items: center; gap: 12px; }
button {
width: 36px; height: 36px;
border: 1px solid #ccc;
border-radius: 50%;
background: white;
cursor: pointer;
font-size: 18px;
}
span { min-width: 30px; text-align: center; font-size: 20px; }
</style>
<button id="dec">-</button>
<span id="value">0</span>
<button id="inc">+</button>
`;
}
}
customElements.define('my-counter', Counter);
<my-counter></my-counter>
<my-counter></my-counter>
Web Component 的优势
| 特性 | 说明 |
|---|---|
| 框架无关 | 在任何项目中都能使用 |
| 样式隔离 | Shadow DOM 确保样式不冲突 |
| 原生支持 | 无需编译、无需 polyfill(现代浏览器) |
| 可复用 | 一次编写,处处使用 |
| 封装 | HTML、CSS、JS 打包在一个文件中 |
16IDC 观察
Web Component 是最佳的前端组件封装方式——不依赖任何框架,可以在任何项目中复用。对于设计系统(Design System)和 UI 组件库来说,Web Component 是比框架特定组件更长寿的选择。如果你的团队需要维护多个不同技术栈的项目,Web Component 是一次编写、处处运行的最佳实践。