原文地址: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. Solr新特性【4.x,5.x,6.x,7.x】

    一.Solr4.x新特性 1.近实时搜索 Solr的近实时搜索[Near Real-Time,NRT]功能实现了文档添加到搜索的快速进行,以应对搜索快速变化的数据. 2.原子更新与乐观并发 原子更新功 ...

  2. prometheus学习系列十一: Prometheus和AlertManager的高可用

    前面的系列中, prometheus和alertmanager都是单机部署的,会有单机宕机导致系统不可用情况发生.本文主要介绍下prometheus和alertmanager的高可用方案. 服务的高可 ...

  3. 锤子手机做appium自动化测试时,运行脚本总是弹出警告框的问题

    当运行脚本的时候,总是提示若干个如上提示框,必须点击同意才能进行下一步:解决方案: 在设置,全局高级设置,开发者选项中打开“总是允许USB安装应用”即可解决:

  4. 利用python jieba库统计政府工作报告词频

    1.安装jieba库 舍友帮装的,我也不会( ╯□╰ ) 2.上网寻找政府工作报告 3.参照课本三国演义词频统计代码编写 import jieba txt = open("D:\政府工作报告 ...

  5. wordpress文章显示同一分类下的上一篇下一篇

    我们在用wordpress开发网站的时候会在文章页中引入上一篇下一篇,但是发现新闻页的上下文章有可能是产品分类的post,这个就不太合理,如何显示同一分类下的上一篇下一篇文章呢?随ytkah一起来看看 ...

  6. Cookies and Custom Protocols

    https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Cookiesa ...

  7. js版本规范的表示:ES6 == ES 6 == ECMAScript 6 == ECMA-262 6

    Ecma 国际大会宣布正式批准ECMA-262第 6 版,亦即 ECMAScript 2015(曾用名:ECMAScript 6.ES6)的语言规范. 关系 ECMA-262 == ECMAScrip ...

  8. DSL的概念

    DSL:以极其高效的方式描述特定领域的对象.规则和运行方式的语言. 需要有特定的解释器与其配合. 高效简洁的领域语言,与通用语言相比能极大降级理解和使用难度,同时极大提高开发效率的语言. 能够描述特定 ...

  9. C# Chart 曲线(多曲线展示)

    //绑定显示曲线数据(Chart控件名:) //X轴标题 this.CurveChart.ChartAreas["ChartArea1"].AxisX.Title = " ...

  10. 简要总结selenium四个工具组

    selenium 是基于WEB的自动化测试工具. 由以下几个工具组组成 1.selenium IDE: 一个火狐插件 点击这个插件就进入录制界面,能够记录用户的操作,并且将其导出为可重复使用的测试脚本 ...