Vue3 + TS 二次封装组件库组件
刀刀
6/19/2025
0 字
0 分钟
- UP主:远方os,视频地址:前往学习
思考
如何更好的封装一个组件库的组件呢?主要从以下三个方面考虑:
props
如何穿透过去- 插槽 如何穿透过去
- 组件内部的方法如何暴露出去
props穿透
props
穿透可以写成以下的方式:
<template>
<Son></Son>
</template>
<template>
<el-input v-bind="$attrs"></el-input>
</template>
这么做虽然可以穿透事件与方法,但是父组件没有代码提示,只能被迫手敲或者翻阅文档,这样做不利于开发。因此需要转换思路。
Element Plus 组件库导出提供了相关的组件类型,我们可以借助这个类型来获取代码提示。<script lang="ts" setup>
import { type InputProps } from 'element-plus'
const props = defineProps<InputProps>()
</script>
<template>
<el-input v-bind="$attrs"></el-input>
<el-input v-bind="props"></el-input>
</template>
现在父组件使用时能看到相应的提示了,但是出现了 TypeScript 的报错,提示参数是必传的,需要使用 TypeScript 的 Partial
类型来包裹一下。 Partial
作用是将类型中的所有属性变为可选。
目前只考虑了属性,还没考虑到事件。事件都在 $attrs
中,因此可以用浅拷贝的方式把 $attrs
和 props
合并一下。
<script lang="ts" setup>
import { type InputProps } from 'element-plus'
const props = defineProps<InputProps>()
const props = defineProps<Partial<InputProps>>()
</script>
<template>
<el-input v-bind="props"></el-input>
<el-input v-bind="{ ...$attrs, ...props }"></el-input>
</template>
插槽穿透
插槽写法可以 v-for
循环 $slots
,循环把插槽挂载到子组件上,但是这么做很繁琐。
可以转变一下思路,不直接使用 <el-input></el-input>
的方式挂载组件,而是通过 <component :is=""></component>
的形式挂载组件。is
不仅可以给一个子组件,还可以给一个 h
函数,因此可以借助 h
函数来挂载插槽。
h
函数第一个参数是组件,第二个参数是属性,第三个参数是插槽。
<script lang="ts" setup>
import { ElInput, type InputProps } from 'element-plus'
import { h } from 'vue'
const props = defineProps<Partial<InputProps>>()
</script>
<template>
<el-input v-bind="{ ...$attrs, ...props }"></el-input>
<component :is="h(ElInput, { ...$attrs, ...props }, $slots)"></component>
</template>
组件方法暴露
组件方法暴露可以使用 ref
来暴露方法。
<script lang="ts" setup>
import { ElInput, type InputProps } from 'element-plus'
import { h, ref } from 'vue'
const props = defineProps<Partial<InputProps>>()
const inputRef = ref()
console.log(inputRef.value)
</script>
<template>
<component :is="h(ElInput, { ...$attrs, ...props, ref: 'inputRef' }, $slots)"></component>
</template>
但是这种方法不可取,如果这个组件绑定了 v-if
,后续存在可能会变动的情况,这样就拿不到 inputRef
的值了。ref
不仅可以赋值字符串,但是还能赋值一个函数,函数的形参接收的就是组件实例。
那么怎么抛出去呢?Vue3 提供了 getCurrentInstance()
方法实例,提供了一个 exposed
方法可以暴露组件实例。因此上方代码可以改写为:
<template>
<Son model-value="111">
<template #prefix></template>
</Son>
</template>
<script lang="ts" setup>
import { ElInput, type InputProps } from 'element-plus'
import { h, getCurrentInstance } from 'vue'
const props = defineProps<Partial<InputProps>>()
const vm = getCurrentInstance()
function changeRef(inputInstance) {
vm.exposed = vm.exposeProxy = inputInstance || {}
}
</script>
<template>
<component :is="h(ElInput, { ...$attrs, ...props, ref: changeRef }, $slots)"></component>
</template>
注意
父组件拿到的不是直接拿 exposed
,而是 exposed
的 exposeProxy
代理对象属性,因此不能只修改 vm.exposed
,还需要修改 vm.exposeProxy
。