Web组件是一套由浏览器原生支持的前端组件化技术规范,允许开发者创建可复用、封装性强的自定义HTML元素,核心包含Custom Elements、Shadow DOM、HTML模板三个部分,无需依赖React、Vue等框架就能实现组件的独立开发和使用。

Web组件的核心组成部分
Custom Elements
Custom Elements是Web组件的基础,允许开发者注册自定义的HTML标签,并且可以为这些标签定义专属的行为和属性,浏览器会识别并正确处理这些自定义标签。
Shadow DOM
Shadow DOM提供了封装能力,自定义元素内部的DOM结构和样式会被隔离,不会和外部的DOM、样式产生冲突,也不会被外部的CSS规则意外影响。
HTML模板
HTML模板使用<template>标签定义,其中的内容不会在页面加载时直接渲染,只有在被克隆并插入到DOM中时才会生效,非常适合作为Web组件的内容模板。
创建自定义HTML元素的完整步骤
第一步:定义组件类
首先需要创建一个继承自HTMLElement的类,在这个类中定义组件的逻辑,包括生命周期回调、属性和方法等。
// 定义自定义元素的类,继承自HTMLElement
class MyCustomElement extends HTMLElement {
// 元素被插入到DOM时触发
connectedCallback() {
this.render();
}
// 渲染组件内容的方法
render() {
// 后续会在这里添加Shadow DOM和模板逻辑
this.innerHTML = '<p>这是我的自定义元素</p>';
}
}
第二步:注册自定义元素
使用customElements.define方法注册自定义元素,第一个参数是自定义标签的名称,要求必须包含连字符,第二个参数是之前定义的组件类。
// 注册自定义元素,标签名必须包含连字符
customElements.define('my-custom-element', MyCustomElement);
注册完成后,就可以在HTML中直接使用这个标签了:
<!-- 直接使用自定义标签 --> <my-custom-element></my-custom-element>
第三步:添加Shadow DOM实现封装
为了让组件的样式和结构隔离,我们可以在组件类的构造函数中创建Shadow DOM,把组件的内容放在Shadow DOM内部。
class MyCustomElement extends HTMLElement {
constructor() {
super();
// 创建开放模式的Shadow DOM
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
render() {
// 把内容渲染到Shadow DOM中
this.shadowRoot.innerHTML = `
<style>
p {
color: #333;
font-size: 16px;
padding: 10px;
border: 1px solid #eee;
}
</style>
<p>这是带Shadow DOM的自定义元素</p>
`;
}
}
// 重新注册元素
customElements.define('my-custom-element', MyCustomElement);
此时组件内部的样式只会作用于Shadow DOM内部的内容,不会影响到外部的元素,外部的样式也不会覆盖组件内部的样式。
第四步:结合HTML模板优化开发
如果组件的内容结构比较复杂,直接使用字符串拼接innerHTML不够直观,可以结合<template>标签来定义组件的内容模板。
<!-- 定义组件模板 -->
<template id="my-element-template">
<style>
.container {
background-color: #f5f5f5;
border-radius: 4px;
padding: 15px;
}
.title {
font-weight: bold;
margin-bottom: 8px;
}
</style>
<div class="container">
<div class="title">自定义组件标题</div>
<div class="content">这里是组件的内容区域</div>
</div>
</template>
class MyCustomElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
// 获取模板内容
const template = document.getElementById('my-element-template');
// 克隆模板内容插入到Shadow DOM中
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
}
customElements.define('my-custom-element', MyCustomElement);
自定义元素的生命周期回调
自定义元素提供了多个生命周期回调方法,方便开发者在不同阶段执行对应的逻辑:
- connectedCallback:元素被插入到DOM树时触发,适合做初始化渲染、添加事件监听等操作
- disconnectedCallback:元素从DOM树中移除时触发,适合做清理工作,比如移除事件监听
- attributeChangedCallback:元素的观察属性发生变化时触发,需要先通过observedAttributes静态属性定义要观察的属性
- adoptedCallback:元素被移动到新的文档时触发,使用场景较少
下面是一个使用属性变化回调的例子:
class MyCustomElement extends HTMLElement {
// 定义需要观察的属性
static get observedAttributes() {
return ['title'];
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
// 属性变化时触发
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'title') {
this.render();
}
}
render() {
const title = this.getAttribute('title') || '默认标题';
this.shadowRoot.innerHTML = `<p>当前标题:${title}</p>`;
}
}
customElements.define('my-custom-element', MyCustomElement);
使用时可以通过修改元素的title属性来更新组件内容:
<my-custom-element title="新标题"></my-custom-element>
<script>
const el = document.querySelector('my-custom-element');
// 修改属性,组件会自动更新
el.setAttribute('title', '修改后的标题');
</script>
Web组件的使用注意事项
- 自定义标签的名称必须包含至少一个连字符,比如
my-button、app-header,不能使用单个单词作为标签名,避免和原生HTML标签冲突 - Shadow DOM的模式分为open和closed,open模式下可以通过元素的shadowRoot属性访问Shadow DOM内容,closed模式下则无法访问,一般开发时建议使用open模式
- 如果组件需要支持老版本浏览器,可能需要引入对应的polyfill,因为部分旧浏览器不支持Web组件的全部特性
- 自定义元素注册之后就不能重复注册,否则会报错,所以注册逻辑一般放在脚本的最前面执行
Web_ComponentsCustom_ElementsShadow_DOMHTML模板修改时间:2026-06-13 04:12:42