Web Components是一套由浏览器原生提供的组件化标准,包含自定义元素、Shadow DOM、HTML模板和HTML导入四个核心特性,其中前三个是构建跨框架UI组件库的核心依赖。它不依赖任何第三方框架,编写好的组件可以在React、Vue、Angular甚至原生HTML项目中直接使用,非常适合需要跨技术栈复用的组件场景。

Web Components核心特性解析
自定义元素
自定义元素允许开发者定义自己的HTML标签,浏览器会识别这些标签并关联对应的逻辑。定义自定义元素需要继承HTMLElement类,然后通过customElements.define方法注册到浏览器中。
Shadow DOM
Shadow DOM可以为组件创建独立的DOM和样式作用域,组件内部的样式不会影响到外部页面,外部页面的样式也不会渗透到组件内部,完美解决了组件样式隔离的问题,是UI组件库避免样式冲突的关键。
HTML模板
HTML模板使用<template>标签定义组件的DOM结构,模板内容不会在页面初始化时渲染,只有在组件被实例化时才会被克隆到Shadow DOM中,提升了组件的渲染效率。
构建基础UI组件示例
下面以一个简单的按钮组件为例,演示如何用Web Components实现一个可复用的UI组件。
// 定义按钮组件类,继承HTMLElement
class MyButton extends HTMLElement {
constructor() {
super();
// 创建Shadow DOM,开启样式隔离
const shadow = this.attachShadow({ mode: 'open' });
// 获取组件属性,设置默认值
const text = this.getAttribute('text') || '默认按钮';
const type = this.getAttribute('type') || 'primary';
// 定义组件的模板结构
const template = document.createElement('template');
template.innerHTML = `
<style>
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.primary {
background-color: #1677ff;
color: white;
}
.default {
background-color: white;
color: #333;
border: 1px solid #d9d9d9;
}
.btn:hover {
opacity: 0.8;
}
</style>
<button class="btn ${type}">${text}</button>
`;
// 将模板内容克隆到Shadow DOM中
shadow.appendChild(template.content.cloneNode(true));
// 绑定点击事件,触发自定义事件供外部监听
shadow.querySelector('.btn').addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('my-click', {
detail: { text: text },
bubbles: true,
composed: true
}));
});
}
// 定义需要监听的属性变化
static get observedAttributes() {
return ['text', 'type'];
}
// 属性变化时的回调逻辑
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) return;
const btn = this.shadowRoot.querySelector('.btn');
if (name === 'text') {
btn.textContent = newValue;
} else if (name === 'type') {
btn.className = `btn ${newValue}`;
}
}
}
// 注册自定义元素,标签名为my-button
customElements.define('my-button', MyButton);
上面的代码实现了一个支持自定义文本和类型的按钮组件,组件内部的样式完全隔离,不会和外部样式冲突。使用时只需要在HTML中直接写<my-button text="提交" type="primary"></my-button>即可。
跨框架使用适配方案
Web Components组件本身是框架无关的,但在不同框架中使用时可能需要做一些适配,让组件更符合框架的使用习惯。
在React中使用
React对自定义元素的支持已经比较完善,但是需要注意事件监听和属性传递的问题。React中监听自定义事件需要使用onMyClick的形式,同时如果需要传递对象类型的属性,需要通过ref直接操作组件实例。
import React, { useRef } from 'react';
function App() {
const btnRef = useRef(null);
const handleClick = (e) => {
console.log('按钮被点击,携带参数:', e.detail.text);
};
return (
<div>
{/* 直接使用自定义元素标签 */}
<my-button
text="React中的按钮"
type="default"
onMyClick={handleClick}
ref={btnRef}
></my-button>
</div>
);
}
export default App;
在Vue中使用
Vue对自定义元素的支持需要先在配置中声明忽略自定义元素,避免编译时报错。Vue3中可以在vite.config.js中配置compilerOptions.isCustomElement,Vue2中可以在vue.config.js中配置ignoredElements。
// Vue3的vite配置示例
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
// 声明my-开头的标签为自定义元素,不报错
isCustomElement: (tag) => tag.startsWith('my-')
}
}
})
]
});
配置完成后就可以在Vue模板中直接使用组件,事件监听使用@my-click,属性传递和原生HTML元素一致。
<template>
<div>
<my-button
text="Vue中的按钮"
type="primary"
@my-click="handleClick"
></my-button>
</div>
</template>
<script setup>
const handleClick = (e) => {
console.log('按钮点击参数:', e.detail);
};
</script>
在Angular中使用
Angular需要在模块中配置CUSTOM_ELEMENTS_SCHEMA,允许使用自定义元素标签,同时可以通过@ViewChild获取组件实例,监听自定义事件。
import { Component, ViewChild, ElementRef } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<my-button text="Angular中的按钮" type="primary"></my-button>
`,
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppComponent {
@ViewChild('myBtn', { static: false }) myBtn!: ElementRef;
ngAfterViewInit() {
// 监听自定义事件
this.myBtn.nativeElement.addEventListener('my-click', (e: any) => {
console.log('Angular中监听按钮点击:', e.detail);
});
}
}
组件库优化建议
- 统一组件属性规范,所有属性都通过
setAttribute和getAttribute操作,避免直接挂载实例属性导致框架适配问题。 - 自定义事件命名统一加业务前缀,比如
my-click、my-change,避免和原生事件冲突。 - 提供类型声明文件,方便TypeScript项目使用,提升开发体验。
- 对于复杂组件,可以结合
<slot>元素实现内容分发,让组件支持更灵活的内容插入。
Web_ComponentsUI组件库跨框架自定义元素修改时间:2026-07-03 07:06:30