Vue3 + TS项目,获取的子组件实例可能为 null 的问题
1,问题表现
子组件
<script setup>
const count = ref(0)
defineExpose({
count
})
</script>
获取子组件实例
1,JavaScript 版本
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const child = ref(null)
const onClick = () => {
// child.value 是 <Child /> 组件的实例
console.log(child.value.count)
}
</script>
<template>
<Child ref="child" />
<button @click="onClick">点击试下</button>
</template>
虽然默认值是 null
,但通过点击事件触发时,child
早已被赋值,所以直接写 child.value.count
是没有问题的。
2,但在 typescript 版本中,会报错
<script setup lang="ts">
import { ref } from 'vue'
import Child from './Child.vue'
// 标注类型
const child= ref<InstanceType<typeof Child > | null>(null)
const onClick = () => {
// 报错,'child.value' is possibly 'null'.ts(18047)
console.log(child.value.count)
}
</script>
<template>
<Child ref="child" />
<button @click="onClick">点击试下</button>
</template>
2,解决问题
2.1,使用非空判断
const onClick = () => {
console.log(child.value?.count)
}
2.2,使用非空断言
const onClick = () => {
console.log(child.value!.count)
}
2.3,使用类型断言
如果不想每次都加 ?
或 !
,可以在定义子组件实例时进行类型断言:
import type { Ref } from "vue";
import Child from './Child.vue'
const child= ref() as Ref<InstanceType<typeof Child>>
2.4,3种方式的区别
断言和非空判断的本质是不一样的。
- 断言是跳过类型检查,后面的代码会被执行。
- 非空判断,后面的代码不一定会被执行。
所以,使用时得注意:
- 断言,只适合使用的子组件实例确定存在时,比如点击事件或
onMounted
生命周期函数中。 - 非空判断,适用性比较广,并且子组件实例不确定是否存在时,只能使用非空判断,比如
computed
计算属性等场景中。
以上。