likes
comments
collection
share

【踩坑记】Vue3父组件调用子组件里方法的常用方式居然行不通了!

作者站长头像
站长
· 阅读数 18

最近在写一个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();
})

结果报错了!

【踩坑记】Vue3父组件调用子组件里方法的常用方式居然行不通了!

Vue2项目中,我也是同样的代码逻辑,只是语法不一样,是可以的。

this.$refs.hroInfoRef.getHroInfo()

为啥到了Vue3项目中,就不能呢?

二、问题解决过程实录

报错分析

【踩坑记】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)。