vue3继承并扩展三方组件完成二次封装

文章使用naiveui的Input组件进行举例,elementPlus或者其他组件库同理

继承emits和props

xml 复制代码
<!-- TInput.vue -->
<script setup lang="ts">
//导入三方组件提供的props
import { NInput, inputProps } from 'naive-ui'
//定义props
const props = defineProps(inputProps)
//扩展默认组件的type类型
const props = defineProps(inputProps)
</script>
<template>
  <NInput v-bind="props" ref="_ref">
  </NInput>
</template>

这样就能完整的继承组件的props和emits了 在另一个文件中检查一下类型是否正确

很完美

扩展emits和props呢

我们利用assign方法来进行扩展 举个例子,为placeholder添加一个前缀,这是原本输入框的清除按钮

xml 复制代码
<!-- TInput.vue -->
<script setup lang="ts">
//导入三方组件提供的props
import { NInput, inputProps } from 'naive-ui'
import { assign } from 'lodash'
import { computed, onMounted, ref } from 'vue'

//扩展placeholder的前缀
const props = defineProps(
  assign(inputProps, {
    placeholder: {
      type: String,
      default: 'default-input'
    },
    suffix: {
      type: String,
      default: ''
    }
  })
)

const _placeHolder = computed(() => props?.suffix + props?.placeholder)
</script>
<template>
   <NInput v-bind="props" :placeholder="_placeHolder" ref="_ref"> </NInput>
</template>
xml 复制代码
//使用TInput组件
<template>

  <TInput v-model:value="val" :suffix="'suffix'"></TInput>

</template>

可以看到我们对 placeholder设置的前缀生效了

继承插槽

我们可以通过useSlots或者$slots来获取到接受到的插槽,然后只要简单的循环就能继承插槽了

xml 复制代码
<template>
  <NInput v-bind="props" ref="_ref" :placeholder="_placeHolder">
    <template v-for="(_, name) in $slots" #[name]="slotData">
      <slot :name v-bind="slotData || {}"></slot>
    </template>
  </NInput>
</template>

扩展插槽

举个例子,我想修改NInput默认的清除按钮,但又保留使用插槽修改清除按钮的功能 这是原本的清除按钮

现在将这个清除按钮的默认值修改为c 为插槽设置默认值

xml 复制代码
<template>
   <NInput v-bind="props" ref="_ref" :placeholder="_placeHolder">
    <template #clear-icon><div>c</div></template>
    <template v-for="(_, name) in $slots" #[name]="slotData" :key="name">
      <slot :name v-bind="slotData || {}"></slot>
    </template>
  </NInput>
</template>

当我 没有使用clear-icon的插槽的时候,可以看到

当我使用clear-icon的插槽的时候

perl 复制代码
  <TInput v-model:value="val" :suffix="'suffix'" clearable>
    <template #clear-icon>
      <div>q</div>
    </template>
  </TInput>

插槽没有类型提示

需要检查你使用的三方组件是否导出了Slots的类型,然后在defineSlots中使用这个类型,使用类型工具扩展.但是由于组件泛型并不好用.定义slots的类型意义不大. 由于naiveui并没有导出Slots的类型,因此这里不做演示

继承组件实例提供的方法

继承组件实例的方法需要在Mounted的生命周期中,获取组件实例并且将其导出

typescript 复制代码
<template>
  <NInput ref="_ref" >
  </NInput>
</template>
<script setup lang="ts">
const _ref = ref()
const _expose: Record<string, any> = {
  getInst: () => {
    return _ref.value
  }
}
defineExpose(_expose)
</script> 

在使用TInput组件的时候引入组件实例的类型,实现良好的类型提示

typescript 复制代码
<script setup lang="ts">
import TInput from './components/TInput.vue'
import { onMounted, ref } from 'vue'
import { type InputInst } from 'naive-ui'
const val = ref()
const inputRef = ref< { getInst: () => InputInst }>()
onMounted(() => {
  console.log(inputRef.value?.getInst?.()?.focus())
})
</script>
<template>
  <TInput ref="inputRef" v-model:value="val" :suffix="'suffix'" clearable> </TInput>
</template>

注意,不要使用循环的方式为expose添加键值对,因为对组件实例的操作会让vue提交警告, `void app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead. 因此简单的把组件实例通过expose的函数导出即可

相关推荐
毕业设计70318 小时前
(免费领源码) 基于微信小程序的预制菜商城的设计与实现25172-java、PHP、python、C#、小程序、大数据、单片机、网络工程等)
vue.js·python·mysql·微信小程序·pycharm·微信开发者工具·推荐算法
xifangge202518 小时前
AGENTS.md 怎么写?涵盖 Java、Python、Vue、Go 的 8 套开箱即用模板
java·vue.js·python
雪芽蓝域zzs19 小时前
vue解构平铺VS对象包裹
前端·javascript·vue.js
Bs_MoneyMagnet1 天前
基于springboot+vue的旅游景区点评系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·旅游
ID34610744201 天前
【课程设计】基于Spring Boot+Vue的校园共享无人机服务系统设计与实现-计算机毕设 附源码44219
javascript·vue.js·spring boot·python·node.js·php·课程设计
BillKu1 天前
Vue3 可拖拽分栏布局:两种实现方式详解
javascript·vue.js·ecmascript
雪芽蓝域zzs1 天前
第三十七节:防抖、节流封装(composables 通用函数,搜索框防重复请求)
前端·javascript·vue.js
Bs_MoneyMagnet1 天前
基于springboot+vue的爱心众筹系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·管理系统
Bs_MoneyMagnet1 天前
基于springboot+vue的在线音乐管理系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring
岁岁种桃花儿1 天前
Vue组件化编程第二篇:非单位件组件
前端·javascript·vue.js