Vue中用props给data赋初始值遇到的问题解决

 更新时间:2018年11月27日 10:09:14   作者:yuyongyu    我要评论

 
这篇文章主要介绍了Vue中用props给data赋初始值遇到的问题解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

前言

前段时间做一个运营活动的项目,上线后产品反馈页面埋点不对,在排查过程中发现,问题竟然是由于Vue中的data初始值导致,而data的初始值来自于props。为方便描述,现将问题抽象如下:

一、现象

代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!DOCTYPE html>
< html lang = "en" >
< head >
   < meta charset = "UTF-8" >
   < title >用props初始化data中变量</ title >
   < script src = "https://cdn.bootcss.com/vue/2.5.16/vue.min.js" ></ script >
</ head >
< body >
< div id = "app" >
   < user-info :user-data = "user" ></ user-info >
</ div >
< script >
   //全局组件
   let userInfo = Vue.component('userInfo' ,{
     name: 'user-info',
     props: {
       userData: Object
     },
     data() {
      return {
        userName: this.userData.name
      }
     },
     template: `
       < div >
         < div >姓名:{{userName}}</ div >
         < div >性别:{{userData.gender}}</ div >
         < div >生日:{{userData.birthday}}</ div >
       </ div >
     `
   });
 
   //Vue实例
   new Vue({
     el: '#app',
     data: {
       user: {
         name: '',
         gender: '',
         birthday: ''
       }
     },
     created(){
       this.getUserData();
     },
     methods:{
       getUserData(){
         setTimeout(()=>{
           this.user = {
             name: '于永雨',
             gender: '男',
             birthday: '1991-7'
           }
         }, 500)
       }
     },
     components: {
       userInfo
     }
   });
</ script >
</ body >
</ html >

代码解读:

  • 根组件data中有一个对象:user,包含三个属性:name、gender、birthday,初始值都为空字符串
  • 模拟api异步请求,500毫秒后对user的重新赋值,三个属性都不再为空
  • 声明一个子组件userInfo,props中有一个对象userData,用于接收父组件的user;data中有一个变量userName,初始值来自于userData.name

结果:

页面初始化后,姓名、性别、生日都显示为空,500毫秒后性别和生日显示正常结果,仅姓名没有变化。

为什么会这样呢?

我最初的想法:user.name是String,属于基本数据类型,用它给子组件data中userName赋值,属于基本数据类型赋值,所以当父组件中user.name变化时,子组件中userName并不会随之变化。

是这样的吗?于是我决定将user.name改为对象,通过引用数据类型赋值,然后观察是否符合预期。代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!DOCTYPE html>
< html lang = "en" >
< head >
   < meta charset = "UTF-8" >
   < title >用props初始化data中变量-对象形式</ title >
   < script src = "https://cdn.bootcss.com/vue/2.5.16/vue.min.js" ></ script >
</ head >
< body >
< div id = "app" >
   < user-info :user-data = "user" ></ user-info >
</ div >
< script >
   //全局组件
   let userInfo = Vue.component('userInfo' ,{
     name: 'user-info',
     props: {
       userData: Object
     },
     data() {
      return {
        userName: this.userData.name
      }
     },
     template: `
       < div >
         < div >姓名:{{userName.text}}</ div >
         < div >性别:{{userData.gender}}</ div >
         < div >生日:{{userData.birthday}}</ div >
       </ div >
     `
   });
 
 
   //Vue实例
   new Vue({
     el: '#app',
     data: {
       user: {
         name: {text: ''},
         gender: '',
         birthday: ''
       }
     },
     created(){
       this.getUserData();
     },
     methods:{
       getUserData(){
         setTimeout(()=>{
           this.user = {
             name: {text: '于永雨'},
             gender: '男',
             birthday: '1991-7'
           }
         }, 500)
       }
     },
     components: {
       userInfo
     }
   });
</ script >
</ body >
</ html >

运行结果:姓名仍然没有值,和第一次结果一样!!!

二、原因

那么,原因到底是什么呢?百思不得解,后来和小伙伴们讨论时,有人提出:会不会因为data在初始化时深拷贝?

我觉得这种解释比较靠谱,于是去收集证据,首先去Vue官网翻了一下关于data的文档,其中:

当看到"递归地"那个词,基本上就能断定上面的推论是正确的,因为深拷贝的核心原理就是递归。

原来,Vue初始化时会递归地遍历data所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter,用于实现双向绑定。官方文档在Reactivity in Depth一章明确有说:

还顺便解释了一下为什么Vue不支持IE8的原因:IE8不支持Object.defineProperty。

三、解决办法

既然因为data深拷贝的原因,data无法随着props的变化而更新,我们很自然的就想到Vue中有监听作用的两个功能:watch、computed。

修改代码如下,观察结果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<!DOCTYPE html>
< html lang = "en" >
< head >
   < meta charset = "UTF-8" >
   < title >解决方案:watch、computed</ title >
   < script src = "https://cdn.bootcss.com/vue/2.5.16/vue.min.js" ></ script >
</ head >
< body >
< div id = "app" >
   < user-info :user-data = "user" ></ user-info >
</ div >
< script >
   //全局组件
   let userInfo = Vue.component('userInfo' ,{
     name: 'user-info',
     props: {
       userData: Object
     },
     data() {
      return {
       userName: this.userData.name
      }
     },
     computed: {
       computedUserName(){
         return this.userData.name
       }
     },
     watch: {
       'userData.name': function (val) {//监听props中的属性
         this.userName = val;
       }
     },
     template: `
       < div >
         < div >姓名(watch):{{ userName }}</ div >
         < div >姓名(computed):{{ computedUserName }}</ div >
         < div >性别:{{ userData.gender }}</ div >
         < div >生日:{{ userData.birthday }}</ div >
       </ div >
     `
   });
 
 
   //Vue实例
   new Vue({
     el: '#app',
     data: {
       user: {
         name: '',
         gender: '',
         birthday: ''
       }
     },
     created(){
       this.getUserData();
     },
     methods:{
       getUserData(){
         setTimeout(()=>{
           this.user = {
             name: '于永雨',
             gender: '男',
             birthday: '1991-7'
           }
         }, 500)
       }
     },
     components: {
       userInfo
     }
   });
</ script >
</ body >
</ html >

运行结果

完美!!!

四、总结:关于Vue中props的要点

事后又仔细翻了一下关于props的文档:

大概梳理一下:

1.props是单向数据流:父组件的数据变化,通过props实时反应在子组件中,反之不然

2.不允许在子组件中直接操作props

3.可以变相操作props

(1)在data中声明局部变量,并用props初始化,弊端:局部变量不随着props更新而更新

(2)在computed中对props值转换后输出

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

 

转载于:https://www.cnblogs.com/mouseleo/p/11135291.html

Logo

前往低代码交流专区

更多推荐