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


Posted in Javascript onNovember 27, 2018

前言

前段时间做一个运营活动的项目,上线后产品反馈页面埋点不对,在排查过程中发现,问题竟然是由于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: '于永雨',
            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

结果:

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

页面初始化后,姓名、性别、生日都显示为空,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: '于永雨'},
            gender: '男',
            birthday: '1991-7'
          }
        }, 500)
      }
    },
    components: {
      userInfo
    }
  });
</script>
</body>
</html>

运行结果:姓名仍然没有值,和第一次结果一样!!!

二、原因

那么,原因到底是什么呢?百思不得解,后来和小伙伴们讨论时,有人提出:会不会因为data在初始化时深拷贝?

我觉得这种解释比较靠谱,于是去收集证据,首先去Vue官网翻了一下关于data的文档,其中:

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

当看到"递归地"那个词,基本上就能断定上面的推论是正确的,因为深拷贝的核心原理就是递归。

原来,Vue初始化时会递归地遍历data所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter,用于实现双向绑定。官方文档在Reactivity in Depth一章明确有说:

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

还顺便解释了一下为什么Vue不支持IE8的原因:IE8不支持Object.defineProperty。

三、解决办法

既然因为data深拷贝的原因,data无法随着props的变化而更新,我们很自然的就想到Vue中有监听作用的两个功能:watch、computed。

修改代码如下,观察结果:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>解决方案:watch、computed</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
     }
    },
    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给data赋初始值遇到的问题解决

完美!!!

四、总结:关于Vue中props的要点

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

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

大概梳理一下:

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

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

3.可以变相操作props

(1)在data中声明局部变量,并用props初始化,弊端:局部变量不随着props更新而更新

(2)在computed中对props值转换后输出

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JQuery 简便实现页面元素数据验证功能
Mar 24 Javascript
javascript 兼容鼠标滚轮事件
Apr 07 Javascript
jquery、js操作checkbox全选反选
Mar 12 Javascript
node.js+express制作网页计算器
Jan 17 Javascript
Javascript 数组去重的方法(四种)详解及实例代码
Nov 24 Javascript
微信小程序 form组件详解及简单实例
Jan 10 Javascript
JS实现两周内自动登录功能
Mar 23 Javascript
利用Three.js如何实现阴影效果实例代码
Sep 26 Javascript
vue中post请求以a=a&amp;b=b 的格式写遇到的问题
Apr 27 Javascript
Vue项目pdf(base64)转图片遇到的问题及解决方法
Oct 19 Javascript
微信小程序学习笔记之表单提交与PHP后台数据交互处理图文详解
Mar 28 Javascript
如何通过JS实现转码与解码
Feb 21 Javascript
使用electron制作满屏心特效的示例代码
Nov 27 #Javascript
30分钟快速实现小程序语音识别功能
Nov 27 #Javascript
基于Koa2写个脚手架模拟接口服务的方法
Nov 27 #Javascript
Vue实现移动端左右滑动效果的方法
Nov 27 #Javascript
vue2.0移动端滑动事件vue-touch的实例代码
Nov 27 #Javascript
详解vuex 渐进式教程实例代码
Nov 27 #Javascript
解决vue 界面在苹果手机上滑动点击事件等卡顿问题
Nov 27 #Javascript
You might like
php数组函数序列之array_intersect() 返回两个或多个数组的交集数组
2011/11/10 PHP
浅析php header 跳转
2013/06/17 PHP
PHP处理JSON字符串key缺少双引号的解决方法
2014/09/16 PHP
Javascript简单实现可拖动的div
2013/10/22 Javascript
Javascript排序算法之合并排序(归并排序)的2个例子
2014/04/04 Javascript
javascript移动设备Web开发中对touch事件的封装实例
2014/06/05 Javascript
Node.js实现兼容IE789的文件上传进度条
2016/09/02 Javascript
全面解析Bootstrap表单样式的使用
2016/09/09 Javascript
Vue组件tree实现树形菜单
2017/04/13 Javascript
Vue.js实现输入框绑定的实例代码
2017/08/24 Javascript
解决layui上传文件提示上传异常,实际文件已经上传成功的问题
2018/08/19 Javascript
Angular5中状态管理的实现
2018/09/03 Javascript
Windows下支持自动更新的Electron应用脚手架的方法
2018/12/24 Javascript
对于防止按钮重复点击的尝试详解
2019/04/22 Javascript
基于js实现复制内容到操作系统粘贴板过程解析
2019/10/11 Javascript
vue实现动态给id赋值,点击事件获取当前点击的元素的id操作
2020/11/09 Javascript
[02:56]DOTA2矮人直升机 英雄基础教程
2013/11/26 DOTA
Python strip lstrip rstrip使用方法
2008/09/06 Python
Python+Pika+RabbitMQ环境部署及实现工作队列的实例教程
2016/06/29 Python
Python数据结构与算法之二叉树结构定义与遍历方法详解
2017/12/12 Python
python程序控制NAO机器人行走
2019/04/29 Python
Python正则表达式匹配数字和小数的方法
2019/07/03 Python
Python 利用高德地图api实现经纬度与地址的批量转换
2019/08/14 Python
使用OpenCV实现仿射变换—旋转功能
2019/08/29 Python
pycharm第三方库安装失败的问题及解决经验分享
2020/05/09 Python
python中的时区问题
2021/01/14 Python
CSS3实现可爱的小黄人动画
2016/07/11 HTML / CSS
Crocs欧洲官网:Crocs Europe
2020/01/14 全球购物
办公室文员自荐书
2014/02/03 职场文书
弄虚作假心得体会
2014/09/10 职场文书
村干部四风问题整改措施
2014/09/30 职场文书
财产保全担保书
2015/01/20 职场文书
三好学生主要事迹材料
2015/11/03 职场文书
2016年劳模先进事迹材料
2016/02/25 职场文书
python APScheduler执行定时任务介绍
2022/04/19 Python
JS轻量级函数式编程实现XDM二
2022/06/16 Javascript