Vue中用props给data赋初始值遇到的问题解决
Vue中用props给data赋初始值遇到的问题解决
更新时间:2018年11月27日 10:09:14   作者:yuyongyu    我要评论
 我要评论
前言
前段时间做一个运营活动的项目,上线后产品反馈页面埋点不对,在排查过程中发现,问题竟然是由于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><htmllang="en"><head>  <metacharset="UTF-8">  <title>用props初始化data中变量</title></head><body><divid="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><htmllang="en"><head>  <metacharset="UTF-8">  <title>用props初始化data中变量-对象形式</title></head><body><divid="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><htmllang="en"><head>  <metacharset="UTF-8">  <title>解决方案:watch、computed</title></head><body><divid="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值转换后输出
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
Vue中用props给data赋初始值遇到的问题解决的更多相关文章
- [转]Vue中用props给data赋初始值遇到的问题解决
		原文地址:https://segmentfault.com/a/1190000017149162 2018-11-28更:文章发布后因为存在理解错误,经@Kim09AI同学提醒后做了调整,在此深表感谢 ... 
- Bash 什么时候会给 HOME 赋初始值
		今天无意发现下面这个表现: $ env -i bash -c cd bash: line 0: cd: HOME not set $ env -i bash -c 'echo $HOME' 这表明了 ... 
- static 和 final 关键字 对实例变量赋初始值的影响
		static 和 final 关键字 对实例变量赋初始值的影响 最近一直在看<深入理解Java虚拟机>,在看完了对象内存分配.Class文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ... 
- (二)用控制器controller给模型数据赋初始值
		之前博客,非常easy的就实现了模型数据和页面显示的自己主动绑定.如今我们使用控制器,给模型赋初始值. 假设使用jquery来实现变量赋初值,须要在页面载入完毕后运行$("#target&q ... 
- C语言赋初始值
- vector 赋初始值的问题
		这个,输出为1 这个,啥都输不出来. 据说是因为没有初始化. 其实我搜了一下 vector<vector<int> > A;//正确的定义方式 vector<vector ... 
- vue & modal props & form data update bug
		vue & modal props & form data update bug OK <div> <BindModal :dialogBindVisible=&qu ... 
- DropdownList 赋初始值问题
		网上查了这样的代码 虽然是可以用.但是会点击多次会出现”“ dropdownList不能选多个值的问题“ private void initdroplistitemlirun(string c_Bus ... 
- 静态Map类型变量赋初始值
		private static Map<String,String> sysTypeList = new HashMap<String, String>(); static { ... 
随机推荐
- 验证码处理+cookie模拟登录
			一.背景 相关博文:https://www.jianshu.com/p/9fce799edf1e https://blog.csdn.net/h19910518/article/details/793 ... 
- Python--context(上下文)(其实是环境)
			最近读Flask的文档,读到很多关于Context(上下文)的术语,如应用上下文,请求上下文等,查阅资料但没有得到理解?有没有比较好的解释? 每一段程序都有很多外部变量.只有像Add这种简单的函数才是 ... 
- PAT(B) 1010 一元多项式求导(Java)
			题目链接:1010 一元多项式求导 代码 /** * Score 25 * Run Time 94ms * @author wowpH * @version 1.1 */ import java.ut ... 
- Python开发【第三章】:编码转换
			一.字符编码与转码 1.bytes和str 之前有学过关于bytes和str之间的转换,详细资料->bytes和str(第四字符串) 2.为什么要进行编码和转码 由于每个国家电脑的字符编码格式不 ... 
- s5p6818 从SD卡启动程序(制作SD启动卡)
			背景: 最近在学习uboot,其中有一步很重要的任务就是需要实现uboot 的验证,没有办法验证uboot是不是自己做的,那么整个开发就会收到阻碍.另外,从公司现在开发的板子来看,uboot从sd卡启 ... 
- python 创建es mapping
			import requests def get_(): url = "http://127.0.0.1:9200/indextest/_mapping?pretty" ss = r ... 
- docker系列3--dockerd配置文件
			dockerd启动配置 docker通信方式选择 docker默认以sock文件方式提供接口,要开放tcp接口远程调用,需要修改配置文件: The Docker daemon can listen f ... 
- Yet Another Problem On a Subsequence CodeForces - 1000D (组合计数)
			大意:定义一个长为$k>1$且首项为$k-1$的区间为好区间. 定义一个能划分为若干个好区间的序列为好序列. 给定序列$a$, 求有多少个子序列为好序列. 刚开始一直没想出来怎么避免重复计数, ... 
- JS强制关闭浏览器页签并且不提示关闭信息
			工作中很多奇葩的需求都会出现,现在就有一个问题,描述如下: 现在的登录跳转权限页面要去掉,集成在第三方系统信息上,当退出登录的时候需要关掉打开的Tab页面,因此考虑使用window.close()关闭 ... 
- VBA if...else语句
			一个if语句由一个布尔表达式和一个或多个语句组成.如果条件评估为True,则执行if条件下的语句.如果条件评估为False,则执行else部分块下的语句. 语法 以下是VBScript中的if els ... 
