原文地址: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. 环境搭建:添加 xgboost 到 Anaconda

    原文参考:https://blog.csdn.net/lvsehaiyang1993/article/details/80619495 原文博主:Big_quant

  2. 【Idea】idea中spring框架配置文件,无法自动提示spring配置

  3. 网络空间安全基础篇(1)----nmap的使用

    nmap工具通俗一点说,就是利用nmap工具来对靶机的所有端口及其开启的服务进行扫描,以便于黑客入侵靶机时,知道用什么漏洞去攻击. nmap常用的命令集(标为红色的一般为常用参数): sV 扫描版本O ...

  4. CentOS6.7编译php5安装错误大全

    在CentOS编译PHP5的时候有时会遇到以下的一些错误信息,基本上都可以通过yum安装相应的库来解决.以下是具体的一些解决办法: 复制代码 代码如下: checking for BZip2 supp ...

  5. find 和grep的区别

    find(以文件属性为查找条件) grep(以文件内容为查找条件) grep works /usr/local/apache/htdocs/index.html 1.将/etc/passwd,有出现 ...

  6. 图论 - PAT甲级 1013 Battle Over Cities C++

    PAT甲级 1013 Battle Over Cities C++ It is vitally important to have all the cities connected by highwa ...

  7. iframe跨域修改子页面内容或样式

    哈哈 当然不能啦 要是能修改百度logo早就是我的头像了

  8. 最近的思考x

    数据结构与文件格式: 自我管理与成长管理: 静态分析与动态分析.

  9. LeetCode 689. Maximum Sum of 3 Non-Overlapping Subarrays

    原题链接在这里:https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/ 题目: In a given arr ...

  10. 小程序SetData