UI组件的二次封装实践指南
在Vue项目中,我们经常需要对第三方UI组件库(如Element UI)进行二次封装,以满足业务需求。本文将介绍如何优雅地实现组件二次封装。
核心概念
1. $attrs
- 包含了父组件传递的所有属性中,未被当前组件props声明的属性
- 在Vue3中,
$attrs同时包含了属性和事件
2. inheritAttrs
- 用于控制是否在组件根元素上继承未被声明的属性
- 设置
inheritAttrs: false可以避免根元素继承不需要的属性,防止出现意外问题(如type被错误修改)
实现示例
Vue2版本实现
html
<template>
<div class="my-input">
<!-- 通过v-bind="$attrs"传递属性,v-on="$listeners"传递事件 -->
<el-input ref="inp" v-bind="$attrs" v-on="$listeners">
<!-- 处理普通插槽 -->
<slot
v-for="(value, name) in $slots"
:name="name"
:slot="name"
></slot>
<!-- 处理作用域插槽 -->
<template
v-for="(value, name) in $scopedSlots"
#[name]="scope"
>
<slot :name="name" v-bind="scope || {}"></slot>
</template>
</el-input>
</div>
</template>
<script>
export default {
inheritAttrs: false,
mounted() {
// 将el-input的方法挂载到当前组件实例上
const entries = Object.entries(this.$refs.inp)
for (const [key, value] of entries) {
this[key] = value
}
}
}
</script>Vue3版本实现
html
<template>
<div class="my-input">
<!-- Vue3中$attrs已包含所有属性和事件 -->
<el-input ref="inp" v-bind="$attrs">
<!-- 统一处理所有插槽 -->
<template
v-for="(value, name) in $slots"
#[name]="slotData"
>
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
</el-input>
</div>
</template>
<script>
export default {
inheritAttrs: false,
mounted() {
// 将el-input的方法挂载到当前组件实例上
const entries = Object.entries(this.$refs.inp)
for (const [key, value] of entries) {
this[key] = value
}
}
}
</script>注意事项
- 设置
inheritAttrs: false很重要,可以避免属性被错误继承 - 在
mounted钩子中复制原组件方法,确保封装后的组件保持原有功能 - 需要正确处理普通插槽和作用域插槽 这样的组件封装方式可以完整保留原组件的功能,同时允许你添加自定义的逻辑和样式。