Skip to content

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>

注意事项 ​

  1. 设置inheritAttrs: false很重要,可以避免属性被错误继承
  2. 在mounted钩子中复制原组件方法,确保封装后的组件保持原有功能
  3. 需要正确处理普通插槽和作用域插槽 这样的组件封装方式可以完整保留原组件的功能,同时允许你添加自定义的逻辑和样式。