原文地址:https://segmentfault.com/a/1190000017149162

2018-11-28更:文章发布后因为存在理解错误,经@Kim09AI同学提醒后做了调整,在此深表感谢。其他不足之处,还望不吝赐教。

前言

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

一、现象

代码:

<!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 = '于永雨';
this.user.gender = '男';
this.user.birthday = '1991-7';
}, 500)
}
},
components: {
userInfo
}
});
</script>
</body>
</html>

代码解读:

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

结果:

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

为什么会这样呢?

二、原因及解决办法

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

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

<!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 = '于永雨';
this.user.gender = '男';
this.user.birthday = '1991-7';
}, 500)
}
},
components: {
userInfo
}
});
</script>
</body>
</html>

运行结果:

完美!!!

如果我们不想把user.name改为Object类型,有没有其他的解决办法呢?

既然基本数据类型赋值没法实现值同步,那我们可以考虑监听props中的值,然后手动变更局部变量。基于此,我们很自然的就想到Vue中有监听作用的两个功能:watch、computed

为了缩减篇幅,我们此处只贴出userInfo组件,其他代码与第一个示例一致,具体如下:

方法一:watch

let userInfo = Vue.component('userInfo' ,{
name: 'user-info',
props: {
userData: Object
},
data() {
return {
userName: this.userData.name
}
},
watch: {
'userData.name': function (val) { //监听props中的属性
this.userName = val;
}
},
template: `
<div>
<div>姓名:{{ userName }}</div>
<div>性别:{{ userData.gender }}</div>
<div>生日:{{ userData.birthday }}</div>
</div>
`
});

方法二:computed

let userInfo = Vue.component('userInfo' ,{
name: 'user-info',
props: {
userData: Object
},
data() {
return {
userName: this.userData.name
}
},
computed: {
computedUserName(){
return this.userData.name
}
},
template: `
<div>
<div>姓名:{{ computedUserName }}</div>
<div>性别:{{ userData.gender }}</div>
<div>生日:{{ userData.birthday }}</div>
</div>
`
});

经验证,结果符合皆预期!

三、走过的弯路

第一条弯路

详见评论区@Kim09AI同学的评论。

第二条弯路

其实,曾以为导致文章开头的问题,是由于data在初始化后深拷贝,props再次变化data并不会刷新导致的。

直到文章发布之初,仍然持此观点,后来经@Kim09AI同学提醒才恍然大悟。

当初之所以深信是data被深拷贝导致的,主要是自己在翻到Vue官方文档看到关于data的描述:

看到"递归地”那个词,就想当然地认为data被深拷贝了,因为深拷贝的核心原理就是递归。

其实现在再回过头来看那段描述,包括在Reactivity in Depth一章的描述:

它们真正含义是:Vue会递归地遍历data所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter,让data中的属性更具“交互性”,以此作为实现双向绑定的基础。包括还顺便解释了一下为什么Vue不支持IE8的原因:IE8不支持Object.defineProperty。

这也仅仅解释了为什么只有在组件初始化之初data中已经声明的属性才具有“交互性”,即data中属性的变化会引起视图变化,而其他在最初data中没有声明的属性则不会。正如在The Vue Instance所说:

小结一下:

  • 文章开头的问题是一个关于基本数据类型和引用数据类型赋值的问题
  • data在初始化时被递归遍历转化是用于实现双向绑定

这么看来,二者是没有任何关系的。

四、关于Vue中props的要点

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

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

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

3.可以变相操作props
(1)在data中声明局部变量,并用props初始化
(2)在computed中对props值转换后输出

五、一点反思

分享是一种知识的传递,严谨和正确是最重要的,技术文章更是如此。想当然和不加深究实为大忌,引以为戒。

[转]Vue中用props给data赋初始值遇到的问题解决的更多相关文章

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

    Vue中用props给data赋初始值遇到的问题解决 更新时间:2018年11月27日 10:09:14   作者:yuyongyu    我要评论   这篇文章主要介绍了Vue中用props给dat ...

  2. vue & modal props & form data update bug

    vue & modal props & form data update bug OK <div> <BindModal :dialogBindVisible=&qu ...

  3. Bash 什么时候会给 HOME 赋初始值

    今天无意发现下面这个表现: $  env -i bash -c cd bash: line 0: cd: HOME not set $ env -i bash -c 'echo $HOME' 这表明了 ...

  4. static 和 final 关键字 对实例变量赋初始值的影响

    static 和 final 关键字 对实例变量赋初始值的影响 最近一直在看<深入理解Java虚拟机>,在看完了对象内存分配.Class文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...

  5. (二)用控制器controller给模型数据赋初始值

    之前博客,非常easy的就实现了模型数据和页面显示的自己主动绑定.如今我们使用控制器,给模型赋初始值. 假设使用jquery来实现变量赋初值,须要在页面载入完毕后运行$("#target&q ...

  6. C语言赋初始值

  7. vector 赋初始值的问题

    这个,输出为1 这个,啥都输不出来. 据说是因为没有初始化. 其实我搜了一下 vector<vector<int> > A;//正确的定义方式 vector<vector ...

  8. 静态Map类型变量赋初始值

    private static Map<String,String> sysTypeList = new HashMap<String, String>(); static { ...

  9. DropdownList 赋初始值问题

    网上查了这样的代码 虽然是可以用.但是会点击多次会出现”“ dropdownList不能选多个值的问题“ private void initdroplistitemlirun(string c_Bus ...

随机推荐

  1. sqlserver一次性修改多条

    修改客户表 编号为 0101007002,0101007003的楼栋号  007-1-102,007-1-201 UPDATE gas_customerSET building= CASEWHEN g ...

  2. ML-求解 SVM 的SMO 算法

    这算是我真正意义上认真去读的第一篇ML论文了, but, 我还是很多地方没有搞懂, 想想, 缓缓吧, 还是先熟练调用API 哈哈 原论文地址: https://www.microsoft.com/en ...

  3. Prometheus学习笔记(7)PromQL玩法入门

    目录 1.什么是PromQL??? 2.如何查询??? 1.什么是PromQL??? PromQL是Prometheus内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持. ...

  4. MATLAB 下GUI的学习

    做界面,然后在对应程序里做调用. 0.打开文件:命令行输入 guide 或者 新建——图形用户界面1.文件-预设可以对字体.代码等进行设置.2.工具-GUI选项可以改变行为大小. 3,添加轴,面板,按 ...

  5. 手抄吧1:windows web server

    字母写的他妈的 太恶心了  以后努力改  天天敲代码  好恶心的字体

  6. 项目Beta冲刺--6/7

    项目Beta冲刺--6/7 作业要求 这个作业属于哪个课程 软件工程1916-W(福州大学) 这个作业要求在哪里 项目Beta冲刺 团队名称 基于云的胜利冲锋队 项目名称 云评:高校学生成绩综合评估及 ...

  7. vue cli4.0 配置环境变量

    温馨提示:本文只适用于vue-cli 3.0 及以上的版本哦~ ------------------正文开始------------------ 开发项目时,经常会需要在不同环境中切换.那么我们如何配 ...

  8. Codechef August Challenge 2019 Chef and Gordon Ramsay

    [传送门] 题目即求所有的三元组,相对大小关系同 $p_1,p_2,p_3$. 题解说都很清楚,这里写一下过程整理一下思路. 如果我们枚举中间这个元素,那么就是统计子树内外有多少个大于这个数和小于这个 ...

  9. PostgreSQL Autovacuum和vacuum

    1 基础知识 重点: 如果您的数据库运行了很久,并且从来没有打开过autovacuum,那么请在打开autovacuum之前全库手动运行vacuum analyze(可能要非常久的时间)完全禁用aut ...

  10. VMware空虚拟机通过网络安装系统时获取不到IP地址情况(基于Linux的DHCP服务器)

    当虚拟机提示no DHCP or proxyDHCP offers were received时 1.dhcp服务配置错误(留意句尾分号“  :”) 2.dhcp服务未启动(用 systemctl s ...