1. 使用场景
我们在子组件中通常需要调用父组件的数据,此时需要使用 vue3 的 props 进行父子组件通信传值。
2. 问题描述
那么此时问题来了,在使用 props 进行父子组件通信时,因为数据传递是异步的,导致子组件无法成功获取数据,若此时父组件中数据发生变化,那子组件获取的值是改变之前的数据。
举个 🌰:
父组件中有一个 tab 进行切换导航,下面的输入框区域为子组件,“个人” clueScope 为 1,“团队” clueScope 为 2,将父组件中的 clueScope 通过 props 传递给子组件,此时会出现下面的结果:
最开始控制台输入一行“个人”的 clueScope,接下来我们点击“团队”输出 1,点击“个人”输出 2,原因是我们在子组件获取的是父组件传递的数据更新之前的值,意识到这个很重要。
这么说有点抽象,简单看一下局部的代码:
父组件
const queryForm = ref({
clueScope: 1, // 默认为 个人 -- 1
}
)
const ChildRef = ref();
const renderTab = () => (
<div>
<ElTabs v-model={queryForm.value.clueScope} onTabChange={handleChangeTab}>
<ElTabPane label='个人' name='1' />
<ElTabPane label='团队' name='2' />
</ElTabs>
</div>
);
// 切换 tab
const handleChangeTab = () => {
ChildRef.value.resetForm();
;
return () => (
<div class=''>
{renderTab()}
<ChildComp
ref={ChildRef}
clueScopeType={queryForm.value.clueScope}
/>
</div>
);
子组件使用 expose 抛出方法,让父组件进行调用。
props: {
clueScopeType: {
type: Number,
default: ClueListTabEnum.个人私池,
},
},
const resetForm = () => {
console.log(props.clueScopeType, 'type');
}
expose({
resetForm,
});
此时就会出现上述问题。
3. 原因
使用 props 进行父子组件通信时,子组件异步获取父组件传递的数据,也就是说,子组件还未获取父组件改变之后的数据,就已经开始执行函数,输出的就是父组件改变之前的值,因此无法成功渲染数据拿到真实值。
4. 解决方法
方法一:watch
最常见的方法就是使用 watch 进行监听,当数据发生改变之后,在执行某项操作。
// 子组件添加 watch
watch(() => props.clueScopeType, (newValue) => {
console.log(props.clueScopeType, 'type');
})
注意 ⚠️:
一般在实际项目开发中,不建议使用 watch,因为后续可能会有其他的功能也涉及到 tab 的变化做一些操作,这样可能会出现逻辑上的耦合,如果我们把多个相互不耦合的函数,都放在一个 watch 里面,那监听的变量越来越多,这个 watch 函数也会越来越大,越来越不好控制,违背了程序的初衷。
方法二:传递参数
沿用刚刚的 resetForm 方法进行修改。
我们在父组件中传递具体的参数值,子组件中可以顺利获取,原因是,在调用函数的时候,父组件传递的值一定是改变后的值,那么子组件获取的也一定是真实的值。
// 父组件
// 切换 tab
const handleChangeTab = () => {
RouteSearchRef.value.resetForm(queryForm.value.clueScope);
};
// 子组件
const resetForm = (type) => {
console.log(type, 'type');
}
expose({
resetForm,
})
以上两个方法都可以成功解决,大家可以根据实际问题具体选择哪一种。