原文地址: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. Spark基于自定义聚合函数实现【列转行、行转列】

    一.分析 Spark提供了非常丰富的算子,可以实现大部分的逻辑处理,例如,要实现行转列,可以用hiveContext中支持的concat_ws(',', collect_set('字段'))实现.但是 ...

  2. flask启动找不到路由问题

    解决方法

  3. mac php thinkphp5 验证码报错 Call to undefined function think\captcha\imagettftext()

    百度一下,是GD库里缺少了freetype支持,然后各种拓展的方法都试了半天,php-v里都生效了,phpinfo里还是不生效,原来是各种文章里都缺少了最关键的一步,修改Apache的配置(我使用的是 ...

  4. Django 之restfromwork 源码---APIView 分析

    Django 之 djangorestframework的APIView分析 APIView 类中的as_view() 方法 首先 我们从视图函数入手,在urls.py 中的 URLconfig中添加 ...

  5. 27、shutil文件操作、xml、subprocess运行子程序模块(了解)

    一.shutil模块(了解):高级的文件.文件夹.压缩包处理模块. import shutil # shutil.copyfileobj(fsrc, fdst[, length]),将文件内容拷贝到另 ...

  6. Spring全家桶相关文章汇总(Spring,SpringBoot,SpringData,SpringCloud)

      因为Spring框架包含的组件比较多,写的博客内容也比较多,虽然有分专栏但是依然不方便查找,所以专门用一篇文章来记录相关文章,会不定期更新. 一.Spring 1.基础内容 Spring介绍 Sp ...

  7. Vue——核心思想--mvvm

    Vue的核心思想为数据驱动和组件化. 一.数据驱动——双向绑定 Vue是一种MVVM框架.而DOM是数据的一个种自然映射.传统的模式是通过Ajax请求从model请求数据,然后手动的触发DOM传入数据 ...

  8. pkusc2019游记

    Day0 早上 6:55 的高铁,6 点就起了,好困呜呜呜 去的路上跟 memset0 坐一起,突然发现雀魂还没停服,先雀了一局(居然拿了个 1 位还飞了一个人),与此同时 memset0 切了一道毒 ...

  9. C#中RadioButton选中和取消选中(一种没办法选中和未选中切换)

    今天在做Winform 中RadioButton控件时,遇到一个小坑,虽然很简单,但是浪费我十分钟,感觉需要记录一下共勉. 问题描述:(RadioButton控件名为rbTime) 本来以为判断一下是 ...

  10. node.js封装数据库增删改查

    数据库增删改查的封装 小编不容易 const sql = { insert: function (Collection, insertData) { return new Promise((resol ...