【踩坑记】Vue3父组件调用子组件里方法的常用方式居然行不通了!
最近在写一个Vue3项目(我的第二个Vue3项目),因为之前写Vue2的项目比较多,所以在写Vue3项目时,还是习惯性地带入Vue2的思维,只不过代码语法,换成Vue3的。
然后,就出幺蛾子了。到底是什么幺蛾子?且听我细细道来。
一、幺蛾子事件
最近Vue3项目里面需要写一个大详情弹框,大详情弹框里面涵盖4个tab,每个tab下的内容、样式均不一样,且内容繁多。
考虑到每个tab里内容繁多且样式不一,为以后方便维护,准备将4个tab下的内容分为四个组件来写,然后再集中到一个主页面里。
因为每个tab内容的接口也是不一样的,所以接口相关逻辑也是在各个组件里写的。接口调用则放在主页面里,当切换至哪个tab,就调用该tab对应的接口。
最终形成了如下代码:
// index.vue
<template>
<div>
<div class="tab-wrap"></div>
<div class="content-wrap">
<tdc-info ref="tdcInfoRef" v-if="state.activeTab === '0'"></tdc-info>
<pta-info ref="ptaInfoRef" v-if="state.activeTab === '1'"></pta-info>
<ats-info ref="atsInfoRef" v-if="state.activeTab === '2'" ></ats-info>
<hro-info ref="hroInfoRef" v-if="state.activeTab === '3'"></hro-info>
</div>
</div>
</template>
<script setup lang="ts">
import TdcInfo from './TdcInfo.vue';
import HroInfo from './HroInfo.vue';
import AtsInfo from './AtsInfo.vue';
import PtaInfo from './PtaInfo.vue';
const tdcInfoRef = ref();
const hroInfoRef = ref();
const atsInfoRef = ref();
const ptaInfoRef = ref();
const state = reactive({
activeTab: '0',
});
const handleTabClick = () => {
switch (state.activeTab) {
case '0':
tdcInfoRef?.value.getTdcInfo();
break;
case '1':
ptaInfoRef?.value.getPtaInfo();
break;
case '2':
atsInfoRef?.value.getAtsInfo();
break;
case '3':
hroInfoRef?.value.getHroInfo();
break;
};
}
</script>
结果运行代码,接口没请求。
想着是用了v-if,dom没获取到,所以又加了一个nextTick(),如下:
nextTick( () => {
hroInfoRef?.value.getHroInfo();
})
结果报错了!
Vue2项目中,我也是同样的代码逻辑,只是语法不一样,是可以的。
this.$refs.hroInfoRef.getHroInfo()
为啥到了Vue3项目中,就不能呢?
二、问题解决过程实录
报错分析
分别打印了hroInfoRef和hroInfoRef?.value的值,发现hroInfoRef?.value的值是undefined的,所以才会报错。
问题解决
-
方法一
将v-if改成v-show,不用加nextTick()。如下:
<hro-info ref="hroInfoRef" v-shwo="state.activeTab === '3'"></hro-info>
v-if和v-show的优缺点此处就不赘述了。
-
方法二
仍旧使用v-if,并将nextTick()换成setTimeout。如下:
setTimeout( () => { hroInfoRef?.value.getHroInfo(); },0)
-
方法三
仍旧使用v-if,并嵌套两个nextTick()。
nextTick( () => { nextTick( () => { hroInfoRef?.value.getHroInfo(); }) })
三、问题升级探索
如果将组件引入方式改成如下,也就是异步加载组件,上述方法还能行得通吗?
const TdcInfo = defineAsyncComponent(() => import('./TdcInfo.vue'));
const HroInfo = defineAsyncComponent(() => import('./HroInfo.vue'));
const AtsInfo = defineAsyncComponent(() => import('./AtsInfo.vue'));
const PtaInfo = defineAsyncComponent(() => import('./PtaInfo.vue'));
我一一测试了下,结论如下:
1.v-show下
TdcInfo组件中的方法不能正常被调用,因该组件是一打开弹框就默认展示的tab。
其他非默认tab的组件,可以正常调用组件内方法。
2.v-if下
- 多个nextTick()嵌套无效,不能正常调用组件内的方法。
- setTimeout根据设定的延时时间不同有不同的结果,测试中0s、10s均不能正常调用组件内的方法,而50s、200s则可以(非默认tab)。
转载自:https://juejin.cn/post/7250083673190219832