抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

当有一个el-dialog在很多组件中都有用到,并且其逻辑一致,这个时候可以考虑把它抽象为一个单独的.vue文件。但此时组件和组件间无法传递ref类型,应该如何绑定v-model来控制组件的开启和关闭呢?使用什么办法给组件传递值呢?

模版引用

如果我们直接使用el-dialog,一般都是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup lang="ts">
const dialogVisible = ref(false)

func openLogic() {
dialogVisible.value = true
}
</script>

<template>
<el-dialog v-model="dialogVisible">
<!-- 逻辑 -->
</el-dialog>
</template>

我们可以直接操作是否显示。这是因为我们能够合理的使用 openLogic()。但如果这个组件内部只有一个el-dialog,即他被整体抽象出来了,我们就没地方执行openLogic()了。

这个时候就在亲组件里使用模版引用吧。以下是componentDialog.vue的核心部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script setup lang="ts">
const dialogVisible = ref(false)

function open() {
dialogVisible.value = true
}

defineExpose({
open
})
</script>

<template>
<el-dialog v-model="dialogVisible">
<!-- 逻辑 -->
</el-dialog>
</template>

向外界暴露了一个open方法。这个时候,在亲组件parent.vue里:

1
2
3
4
5
6
7
8
9
10
11
<script setup lang="ts">
const dialogRef = ref(null)

function openLogic() {
dialogRef.value.open()
}
</script>

<template>
<componentDialog ref="dialogRef"/>
</template>

我们通过Vue的模版引用拿到了这个组件的引用。这样就可以执行组件暴露出的方法并完成openLogic()了。至于关闭,直接在子组件内定义即可。

问题 - ref的时机

假设我的componentDialog.vue需要一些值,我们一般会想到使用props传递数据。那么当DOM被渲染的时候,组件就会试图拿到parent.vue中的数据,而这些数据可能在parent.vue执行一些逻辑后的后续的操作中才被初始化。

我遇到了这个问题,每次我打开parent.vue,子组件就会立刻被初始化,并且通过未初始化的值向后端发送请求,这导致了一些错误。我下意识的想法是使用v-if

然而,模版引用并不是响应式的ref只在DOM渲染的时候去获取对应的组件,然而最开始v-if是false,这就导致引用类型没有正常初始化,因为DOM也没有被渲染。当把v-if设定为true之后就去使用引用类型,则会报错。因为此时引用类型是undefined。这个坑对于v-for也是存在的。

我的解决方案是,仍然沿用问题一的逻辑,子组件暴露的函数需要一些参数,而亲代将需要的数据作为函数参数进行传递,这就推迟了子组件向后端索要数据的时机,从而避免错误。这样就弃用了亲传子常用的props属性。

组件事件

子组件如何通知亲代组件关于某件事情的行为?使用组件事件即可。

子组件可以自定义一些事件,并在合适的时机触发这些事件。亲组件可以自定义一些函数和子组件的事件绑定。当子组件的事件被触发,亲组件相应绑定的函数就会被执行。

举例如下, componentDialog.vue

1
2
3
4
5
6
7
<script>
const emit = defineEmits(['operationFinish'])

func emitLogic() {
emit("operationFinish", ...) // 可选参数
}
</script>

parent.vue

1
2
3
4
5
6
7
8
9
10
11
<script setup lang="ts">

// 同样可选参数
function handleLogic(...) {
// do sth
}
</script>

<template>
<componentDialog @operation-finish="handleLogic"/>
</template>

你可以定义很多emit。这就完成了子传亲的行为。

评论