Web Components是一套由浏览器原生支持的组件化标准,包含Custom Elements、Shadow DOM、HTML Templates、HTML Imports四个核心技术,其中前三个是目前主流浏览器均已支持的特性,能够帮助我们构建不依赖任何前端框架的独立组件,实现跨框架复用。

Web Components核心技术介绍
Custom Elements 自定义元素
自定义元素允许我们注册新的HTML标签,浏览器会识别这些标签并触发对应的生命周期回调,我们可以在回调中定义组件的逻辑。注册自定义元素需要使用customElements.define方法,第一个参数是标签名,要求必须包含连字符,第二个参数是组件的类,该类需要继承HTMLElement。
Shadow DOM 影子DOM
Shadow DOM能够为组件创建独立的DOM和样式作用域,组件内部的DOM结构和样式不会影响到外部页面,外部页面的样式也不会侵入组件内部,完美实现组件的隔离。我们可以通过元素的attachShadow方法开启Shadow DOM,参数为{ mode: 'open' }表示外部可以通过元素的shadowRoot属性访问影子DOM。
HTML Templates 模板
HTML模板使用template标签定义,模板中的内容不会在页面加载时渲染,只有在被克隆并插入到DOM中时才会生效,适合用来定义组件的内部结构,我们可以提前写好模板,在组件初始化时克隆模板内容插入到Shadow DOM中。
构建跨框架复用的业务组件示例
我们以一个简单的用户信息展示业务组件为例,这个组件接收一个用户ID,调用接口获取用户信息并展示,同时支持自定义展示样式,接下来逐步实现这个组件。
第一步:定义组件模板
首先在HTML中定义组件的模板,包含基本的结构和默认样式:
<template id="user-info-template">
<style>
.user-card {
border: 1px solid #e5e5e5;
border-radius: 8px;
padding: 16px;
width: 300px;
font-family: sans-serif;
}
.user-name {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
.user-email {
color: #666;
font-size: 14px;
}
.loading {
color: #999;
}
</style>
<div class="user-card">
<div class="user-name"></div>
<div class="user-email"></div>
</div>
</template>
第二步:定义自定义元素类
接下来定义组件的类,继承HTMLElement,在构造函数中初始化Shadow DOM,克隆模板内容插入,同时定义组件的生命周期和属性监听逻辑:
class UserInfoCard extends HTMLElement {
constructor() {
super();
// 开启Shadow DOM
this.attachShadow({ mode: 'open' });
// 获取模板并克隆内容
const template = document.getElementById('user-info-template');
const content = template.content.cloneNode(true);
this.shadowRoot.appendChild(content);
// 获取内部DOM元素
this.nameEl = this.shadowRoot.querySelector('.user-name');
this.emailEl = this.shadowRoot.querySelector('.user-email');
this.cardEl = this.shadowRoot.querySelector('.user-card');
}
// 定义需要监听的属性
static get observedAttributes() {
return ['user-id', 'theme-color'];
}
// 属性变化时的回调
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'user-id' && newValue) {
this.fetchUserInfo(newValue);
}
if (name === 'theme-color' && newValue) {
this.cardEl.style.borderColor = newValue;
}
}
// 组件插入到DOM时的回调
connectedCallback() {
const userId = this.getAttribute('user-id');
if (userId) {
this.fetchUserInfo(userId);
}
}
// 获取用户信息的方法
async fetchUserInfo(userId) {
try {
this.nameEl.textContent = '加载中...';
this.emailEl.textContent = '';
// 模拟接口请求,实际项目中替换为真实接口地址
const response = await fetch(`https://ipipp.com/api/user/${userId}`);
const user = await response.json();
this.nameEl.textContent = user.name;
this.emailEl.textContent = user.email;
} catch (error) {
this.nameEl.textContent = '加载失败';
this.emailEl.textContent = '';
}
}
}
第三步:注册自定义元素
使用customElements.define方法注册组件,之后就可以在任意页面中使用这个标签:
customElements.define('user-info-card', UserInfoCard);
在不同框架中使用该组件
由于Web Components是浏览器原生支持的,所以可以在任何前端框架中直接使用,不需要额外的适配。
在原生HTML中使用
直接在HTML中写入标签即可,通过属性传递参数:
<user-info-card user-id="1001" theme-color="#1890ff"></user-info-card>
在Vue中使用
Vue默认支持自定义元素,不需要额外配置,直接在模板中使用:
<template>
<div>
<user-info-card :user-id="userId" theme-color="#52c41a"></user-info-card>
</div>
</template>
<script>
export default {
data() {
return {
userId: '1002'
};
}
};
</script>
在React中使用
React中也可以直接使用自定义元素,注意属性名如果是驼峰式需要转换为连字符形式,或者通过元素的setAttribute方法设置:
import React from 'react';
function App() {
const userId = '1003';
return (
<div>
<user-info-card user-id={userId} theme-color="#fa541c"></user-info-card>
</div>
);
}
export default App;
在Angular中使用
Angular需要在模块中配置CUSTOM_ELEMENTS_SCHEMA来允许使用自定义元素:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
bootstrap: [AppComponent]
})
export class AppModule {}
之后在组件的模板中直接使用:
<user-info-card user-id="1004" theme-color="#722ed1"></user-info-card>
注意事项
- 自定义元素的标签名必须包含连字符,避免和原生HTML标签冲突。
- 如果组件需要支持表单场景,需要实现相关的表单关联API,比如
FormAssociated接口。 - 旧版本浏览器(如IE)不支持Web Components,如果需要兼容可以使用对应的polyfill库。
- 组件内部的事件需要通过自定义事件派发出去,外部框架才能监听到,比如组件内部触发
this.dispatchEvent(new CustomEvent('user-load', { detail: user })),外部就可以通过addEventListener监听这个事件。
Web_Components跨框架复用业务组件自定义元素修改时间:2026-07-13 23:24:13