问题形容
组件化开发中常常用到父子组件的通信,父传子子传父等数据的操作,如果父组件的数据是发申请从后端获取的异步数据,那么父组件将这个数据传递给子组件的时候,因为是异步数据,就会呈现父组件传递过来了,然而子组件 mounted 钩子初始状况下是接管不到的问题。本篇文章记录了一下这个问题的解决方案。
在说这个问题之前,咱们先来回顾一下父子组件的生命周期
父子组件生命周期执行程序
加载渲染数据过程
父组件 beforeCreate –>
父组件 created –>
父组件 beforeMount –>
子组件 beforeCreate –>
子组件 created –>
子组件 beforeMount –>
子组件 mounted –>
父组件 mounted –>
更新渲染数据过程
父组件 beforeUpdate –>
子组件 beforeUpdate –>
子组件 updated –>
父组件 updated –>
销毁组件数据过程
父组件 beforeDestroy –>
子组件 beforeDestroy –>
子组件 destroyed –>
父组件 destroyed
能够这样了解,父组件生命周期中会先看看子组件的生命周期有没有走完,子组件生命周期走完了,才会走父组件的生命周期。
问题剖析
咱们模仿一下父子组件通信的过程,写个小 demo。看看在子组件中的 mounted 钩子中能不能接管到父组件传递过去的数据
父组件代码
<template>
<div id="app">
<child :msg="msg"></child>
</div>
</template>
<script>
import child from "./views/child";
export default {
name: "App",
components: {child,},
data() {
return {msg: "", // 咱们要把父组件从接口获取的数据存到 data 中的 msg 外面,而后再传递给子组件};
},
created() {
// 用定时器模仿发申请异步获取后端接口的数据
setTimeout(() => {this.msg = "666";}, 200);
},
};
</script>
子组件代码
<template>
<div>
<h2>{{msg}}</h2>
</div>
</template>
<script>
export default {
props:{
msg:{
type:String,
default:''
}
},
mounted() {console.log('mounted 钩子中接管',this.msg);
},
}
</script>
最终在 mounted 钩子中会实现,咱们会发现打印不进去,如下图
当然如果是同步的数据传递给子组件,子组件的 mounted 钩子是能接管到,能打印进去的,这里就不演示了,因为咱们做我的项目开发的数据大多数都输从后端的接口中获取的异步数据的。
因为父组件传递给子组件的数据,可能咱们还要加工一下再应用,所以在 mounted 钩子中获取父组件传递过去的数据是肯定要做的。那么,这里为什么 mounted 钩子中打印不进去父组件传递过去的数据,然而 props 最终接管到了,页面最终还渲染进去了么?
起因浅析
咱们晓得,mounted 钩子默认加载只会执行一次,因为数据是要等到 200 毫秒当前能力拿到,那么子组件的 mounted 钩子执行的时候,还没有拿到父组件传递过去的数据,然而又必须要打印进去 this.msg
的后果,那这样的话,就只能去打印 props 中的 msg 的默认值空字符串了,所以打印的后果是一个空字符串,比方,咱们在子组件中这样打印就晓得 this.msg
是不是空字符串了
mounted() {console.log('mounted 钩子中接管', this.msg == '');
},
打印后果图如下
然而 props 是能够等的,是能够拿到异步的数据渲染的。所以就呈现了上述的后果,有问题解决问题,接下来说一下解决这样的问题的计划
计划一 应用 v -if 管制子组件渲染的机会
思路其实很简略,就是初始还没拿到后端接口的异步数据的时候,不让组件渲染,等拿到的时候再去渲染组件。应用 v-if="变量"
去管制,初始让这个变量为 false,这样的话,子组件就不会去渲染,等拿到数据的时候,再让这个变量变成 true,这样的话,组件就会去渲染,此时数据也曾经失去了,这样的话,在子组件的 mounted 钩子中就拿到父组件传过来的异步数据了。代码如下
父组件
<template>
<div id="app">
<child :msg="msg" v-if="isGetData"></child>
</div>
</template>
<script>
import child from "./views/child";
export default {
name: "App",
components: {child,},
data() {
return {
msg: "",
isGetData:false // 初始为 false,就不会被渲染对应的子组件
};
},
created() {
// 用定时器模仿发申请异步获取后端接口的数据
setTimeout(() => {
this.msg = "666";
this.isGetData = true // 拿到数据当前,再把 isGetData 置为 true,这样的话,组件就会被渲染啦,数据也就会被传递过来啦
}, 200);
},
};
</script>
子组件
这种形式,子组件不必动代码,在父组件中去做管制即可
然而这种形式有一个小小的毛病,就是最终成果会显得组件有些提早才呈现成果。因为异步数据是从后端的接口获取的,如果接口工夫长一些的话,最终成果渲染也会慢一点,然而!!!个别状况下,后端的接口速度都会管制在几十到几百毫秒的工夫,个别状况下,不会呈现好几秒,甚至几十秒的接口,所以瑕不掩瑜,这种形式不影响咱们应用
计划二 子组件应用 watch 监听父组件传递过去的数据
父组件
这种形式父组件失常传递数据即可,不须要做什么代码解决,只有在子组件中加一个监听即可
子组件
<template>
<div>
<h2>{{editMsg}}</h2>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: "",
},
},
watch: {
// 监听到父组件传递过去的数据后,加工一下,// 存到 data 中去,而后在页面上应用
msg(newnew, oldold) {console.log("监听", newnew, oldold);
this.editMsg = "---" + newnew + "---";
},
},
data() {
return {editMsg: "",};
},
};
</script>
看一下这种形式对应的效果图
看被加工的父组件传递过去的数据
计划三 不应用 props 形式父子组件通信
比方应用事件总线、应用 vuex,不过个别状况下,父子组件通信都是应用 props 通信,所以,解决问题的形式,计划一、计划二任选一种即可。