HTML5 入门教程
Web Components 自定义元素
本教程共 110 篇 · 第 97 篇 · 更新于 2026-07-28 · 约 5 分钟阅读
HTML5HTML5 入门教程Web Components自定义元素Shadow DOM模板
97. Web Components 自定义元素
本节目标:了解 Web Components 是什么,学会创建自定义 HTML 标签,理解 Shadow DOM 的样式隔离作用。
什么是 Web Components
Web Components 是一套浏览器原生技术,让你能创建可复用的自定义 HTML 标签。
三大核心技术:
- Custom Elements:定义新标签
- Shadow DOM:样式和 DOM 隔离
- HTML Templates:定义可复用的 HTML 片段
NoteWeb Components 是原生技术,不依赖任何框架。Vue、React 组件是框架级的,Web Components 是浏览器级的。
创建自定义元素
最简示例
class MyButton extends HTMLElement {
constructor() {
super();
this.innerHTML = "<button>我是自定义按钮</button>";
}
}
customElements.define("my-button", MyButton);
定义后就能在 HTML 里用:
<my-button></my-button>
带属性的自定义元素
class UserCard extends HTMLElement {
constructor() {
super();
}
// 监听哪些属性变化
static get observedAttributes() {
return ["name", "avatar"];
}
// 属性变化时触发
attributeChangedCallback(name, oldVal, newVal) {
this.render();
}
// 元素插入 DOM 时触发
connectedCallback() {
this.render();
}
render() {
const name = this.getAttribute("name") || "匿名";
const avatar = this.getAttribute("avatar") || "default.png";
this.innerHTML = `
<div class="card">
<img src="${avatar}" width="50">
<span>${name}</span>
</div>
`;
}
}
customElements.define("user-card", UserCard);
使用:
<user-card name="张三" avatar="zhang.png"></user-card>
<user-card name="李四"></user-card>
Shadow DOM:样式隔离
Shadow DOM 让组件内部的样式不影响外部,外部样式也不影响内部。
class FancyBox extends HTMLElement {
constructor() {
super();
// 创建 Shadow DOM(closed 或 open)
const shadow = this.attachShadow({ mode: "open" });
shadow.innerHTML = `
<style>
/* 这里的样式只在这个组件内生效 */
.box {
border: 2px solid #3498db;
padding: 20px;
border-radius: 8px;
background: #ebf5fb;
}
</style>
<div class="box">
<slot></slot> <!-- 插槽:显示标签内的内容 -->
</div>
`;
}
}
customElements.define("fancy-box", FancyBox);
使用:
<fancy-box>这里的内容会显示在插槽里</fancy-box>
TipShadow DOM 的样式隔离是双向的。组件内部样式不影响外部,外部 CSS 也进不去(除了 CSS 变量和
::part()伪元素)。
HTML Templates
<template> 标签里的 HTML 不会渲染,等 JS 激活后才用。
<template id="my-tpl">
<style>
.greeting { color: blue; font-size: 20px; }
</style>
<div class="greeting">你好,<span class="name"></span>!</div>
</template>
<script>
class GreetingBox extends HTMLElement {
constructor() {
super();
const tpl = document.getElementById("my-tpl");
const content = tpl.content.cloneNode(true);
content.querySelector(".name").textContent = this.getAttribute("name");
this.appendChild(content);
}
}
customElements.define("greeting-box", GreetingBox);
</script>
使用:
<greeting-box name="世界"></greeting-box>
生命周期回调
自定义元素有几个关键的生命周期钩子:
| 回调 | 触发时机 |
|---|---|
constructor | 元素创建时 |
connectedCallback | 元素插入 DOM 时 |
disconnectedCallback | 元素从 DOM 移除时 |
attributeChangedCallback | 监听的属性变化时 |
adoptedCallback | 元素被移到新文档时 |
class LifeCycle extends HTMLElement {
constructor() {
super();
console.log("1. 创建");
}
connectedCallback() {
console.log("2. 插入页面");
}
disconnectedCallback() {
console.log("3. 从页面移除");
}
static get observedAttributes() {
return ["value"];
}
attributeChangedCallback(name, old, val) {
console.log(`4. ${name} 从 ${old} 变成 ${val}`);
}
}
实际用途
- 跨框架组件库:一套组件在 Vue、React、Angular 里都能用
- 设计系统:公司级 UI 组件,不依赖特定框架
- 微前端:不同团队用不同框架,通过 Web Components 集成
WarningWeb Components 虽然强大,但生态不如 Vue/React 成熟。事件系统、表单集成、SSR 等方面还有坑。大型项目用框架组件更省心,跨框架共享组件时再考虑 Web Components。