- Published on
用 Web Components 封装通用组件,并在 Vue 和 React 中复用
- Authors
- Name
- Violet Chen
Web Components 是封装通用组件的好工具,能让代码跨框架复用,减少重复工作。基于 HTML5 的 Custom Elements、Shadow DOM 和 HTML Templates,组件像原生标签一样用。
封装一个通用组件
假设我们封装一个自定义按钮组件,支持自定义文本和点击事件。核心是用 customElements.define
注册。
组件代码
写个 JavaScript 文件 custom-button.js
:
class CustomButton extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({ mode: 'open' })
// 模板
const template = document.createElement('template')
template.innerHTML = `
<style>
button {
background: #007bff;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
</style>
<button><slot>Default Text</slot></button>
`
shadow.appendChild(template.content.cloneNode(true))
// 事件监听
this.button = shadow.querySelector('button')
this.button.addEventListener('click', () => {
this.dispatchEvent(
new CustomEvent('custom-click', { detail: { message: 'Button clicked!' } })
)
})
}
// 属性观察
static get observedAttributes() {
return ['disabled']
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'disabled') {
this.button.disabled = newValue !== null
}
}
}
customElements.define('custom-button', CustomButton)
这个组件用 Shadow DOM 隔离样式,<slot>
插槽支持自定义内容。事件用 dispatchEvent
抛出自定义事件。
在 Vue 中复用
Vue 3 支持直接用 Web Components,不用额外插件。导入组件 JS 文件,然后像用标签一样。
Vue 配置
在 main.js
或组件中导入:
import './custom-button.js'
Vue 组件中使用:
<template>
<custom-button @custom-click="handleClick">Vue Button</custom-button>
</template>
<script setup>
const handleClick = (event) => {
console.log(event.detail.message) // 'Button clicked!'
}
</script>
Vue 会自动识别自定义元素。事件用 @custom-click
,属性用 :disabled="true"
绑定。
优化
用 Vue 的 v-model
或 props 时,通过 attributeChangedCallback
处理属性变化。测试兼容性,用 vue.config.js
配置忽略自定义元素:
module.exports = {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => ({
...options,
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('custom-'),
},
}))
},
}
在 React 中复用
React 也支持 Web Components,直接导入 JS 文件,用 JSX 标签。
React 配置
在 App.js
或组件中导入:
import './custom-button.js'
使用:
function App() {
const handleClick = (event) => {
console.log(event.detail.message) // 'Button clicked!'
}
return (
<custom-button onCustomClick={handleClick} disabled={true}>
React Button
</custom-button>
)
}
事件用驼峰式 onCustomClick
,属性用小写。React 17+ 支持自定义事件监听。
优化
用 ref
操作组件:
const buttonRef = useRef(null)
useEffect(() => {
buttonRef.current.addEventListener('custom-click', handleClick)
return () => buttonRef.current.removeEventListener('custom-click', handleClick)
}, [])
;<custom-button ref={buttonRef}>Button</custom-button>
避免 React 警告,用 suppressHydrationWarning
或忽略自定义元素警告。
总结
Web Components 封装通用组件简单,跨 Vue 和 React 复用省事。重点是 Custom Elements 注册、Shadow DOM 隔离和事件分发。优化属性观察和事件处理,就能建高效组件库。