前言 📝

在Vue3中,我们会通过提供的defineExpose宏,将内部的方法暴露给父组件使用。如果要是基于某个组件进行二次封装的呢?我们改如何优雅的将该组件暴露的方法提供给最外层组件使用呢?

1. DefineExpose

默认情况下,通过模板引用获取到的组件的公开实例,并不会暴露任何在 <script setup> 中声明的绑定。使用 <script setup> 的组件默认关闭了这个功能。

要显式指定在 <script setup> 组件中要暴露出去的属性,可以使用 defineExpose 编译器宏。下面是一个示例:

1
2
3
4
5
6
7
8
9
10
11
<script setup>
import { ref, defineExpose } from 'vue'

const a = 1
const b = ref(2)

defineExpose({
a,
b
})
</script>

当父组件通过模板引用的方式获取到当前组件的实例时,获取到的实例将包含暴露的属性,例如:{ a: number, b: number }注意,ref 会像在普通实例中一样被自动解包。

1.1 优雅的将被二次封装组件的内部方法暴露出来

deineExpose接收的是一个对象,我们可以尝试给他一个代理对象试一下:

1
2
3
4
5
6
7
8
9
defineExpose(
new Proxy(
{},
{
get(target, key) {
// 我们将一个空的代理对象暴露出去,在拦截的时候,我们可以将组件实例暴露出去
}
})
)

正如上面所示,我们尝试返回一个代理对象,在我们get拦截的时候,我们返回的是被封装组件暴露的方法。

我们尝试在一个真正的例子中使用一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 父组件
<script setup lang="ts">
// 框架
import { ref } from 'vue'
// 组件
import Comp from './Comp.vue'
// 方法/类型

const testVal = ref()
const compRef = ref()

</script>

<template>
<div id="container">
<comp ref="byElInputRef" v-model="testVal" @vue:mounted="(e) => compRef.focus()" />
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script setup lang="ts">
// 框架
import { ElInput } from 'element-plus'
import { ref, defineExpose } from 'vue'
// 组件
// 方法/类型

const inputRef = ref()

defineExpose(
new Proxy(
{},
{
get(target, key) {
return inputRef.value?.[key]
},
has(target, key) {
return key in inputRef.value
}
})
)
</script>

<template>
<el-input ref="inputRef" v-bind="$attrs" />
</template>

我们在父组件中,调用了二次封装ElInput组件的focus方法。在元素挂在完之后,让输入框自动进行聚焦。

成果展示:

进入到页面之后,很明显,自动聚焦了。说明我们的方案是行得通的。

image.png