起因:由父组件将一个数组传入子组件,子组件内通过传入的数组的元素来初始化自身的data。
props: {
// 日期选项
dateOptions: {
type: Array,
default () {
return [];
}
},
......
}
data(){
return{
// 单日日期
date: this.dateOptions[0],
......
}
}
依照Vue官方文档中,使用prop传入的数据进行初始化的使用方法如上图所示(官方文档中并没有对数组某个元素的使用)。但如上方式初始化的date变量为undefined。
以下是在mounted生命周期中在控制台输出该组件实例。
直接在data中初始化不生效,又尝试在mounted中进行初始化依旧如此。
mounted(){
let _this = this;
console.log(_this);
_this.date = _this.dateOptions[0];
console.log(_this.dateOptions[0]);
console.log(_this.date);
......
}
在官方文档和网络上找了挺久,很少有相关的信息(指使用prop传入的数组的一个元素来初始化)。最终在一个 问答 中找到了大概的原因,但他并未给出解决办法。由于没有去啃源码,还有相关信息过少,博主暂时只能解决该问题,核心原理不太清楚,希望能得到大佬解答。按照博客的说法是输出的时候(即初始化的时候),prop传入的是参数暂时是引用,没有值,之后的某个时刻才会获得值。
于是通过setTimeout定时器函数来执行初始化的代码。
mounted(){
let _this = this;
setTimeout(()=>{
// 单日日期
_this.date = _this.dateOptions[0];
}, 0);
......
},
就算定时器设置为0ms,它依然能获得值并成功初始化。
(更新)----以下为原理和官方解决办法
1. 解决办法
在之后的开发中,由于需要整个子组件需要复用并动态添加,使用了v-for的方式遍历加载,偶然间的一次测试发现,不使用setTimeout定时器,初始化也完成了,在控制台也能正常输出。
首先列出官方给出的解决方法:官方文档中的实例方法(以下只列出了基本概念)
这个nextTick方法如上所示,它的功能是将回调延迟到数据更新时执行。用这个方法替换之前不够官方的setTimeout函数,如下:
mounted(){
let _this = this;
this.$nextTick(function () {
// 单日日期
_this.date = _this.dateOptions[0];
......
});
},
以上方法就能够解决遇到的这种问题。
2. 产生原因及其原理
这个问题的发生与生命周期有密切的关联(我也就是再次仔细看Vue实例的生命周期的时候发现的解决办法)如下为涉及的部分生命周期:
先说产生过程。一下为父组件的HTML部分代码
<!-- 父组件 -->
<template>
.......
<!-- 子组件 -->
<Charts
:id="charts[0]"
:time-mode="timeMode"
:date-options="dateOptions"
:ap-options="APOptions"
></Charts>
.......
</template>
由于子组件是直接被使用在父组件的模板代码中,所以在父组件实例化mount的过程中(对应生命周期为beforeMount之后,mounted之前),子组件Chart也开始mount。所以在子组件mounted钩子函数执行时,父组件的dateOptions传入子组件时还是引用,并未获取其内部元素值。
然而,浏览器对象console使用log方法打印出来的数组,它能够在之后获得值时更新在控制台,所以在控制台打印出来的数组,点开详情能够看到其内部的值,但直接打印数组内部某个元素的值时是undefined。
在父组件mounted之后,数据的更新开始响应时,同时也反映到子组件上,所以之前使用setTimeout定时器延时一小段时间能够生效也就是因为一小段时间之后父组件子组件的数据已经更新完成,已经能够通过下标得到实际的值了。
之前提到的Vue实例方法nextTick,便是将回调延迟到数据更新之后再执行,完美解决问题。不过这样问题发生的根本原因是子组件直接被使用在了模板代码中,在父组件实例化的过程中子组件同时开始实例化。我之所以发现问题并想通是因为后续开发中,子组件变成了遍历动态加载的方式。
<!-- 父组件 -->
<template>
.......
<!-- 子组件 -->
<Charts
v-for="chart in charts"
:key="chart"
:id="chart"
:time-mode="timeMode"
:date-options="dateOptions"
:ap-options="APOptions"
></Charts>
.......
</template>
charts这个变量初始为空数组,在父组件mount的过程中,子组件并未被使用,直到在父组件mounted钩子函数中,在charts中新增了元素,才开始进行子组件的实例化,此时父组件数据已经更新完成,子组件便可以正常获取传入的参数的值,在data中定义时都可以直接使用下标正常初始化。
以上都是博主自己的理解,若有任何不同看法欢迎探讨。