zoukankan      html  css  js  c++  java
  • Vue中用props给data赋初始值遇到的问题解决

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

     更新时间:2018年11月27日 10:09:14   作者:yuyongyu    我要评论

     
    这篇文章主要介绍了Vue中用props给data赋初始值遇到的问题解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    前言

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

    一、现象

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>用props初始化data中变量</title>
    </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

    结果:

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

    为什么会这样呢?

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>用props初始化data中变量-对象形式</title>
    </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初始化时会递归地遍历data所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter,用于实现双向绑定。官方文档在Reactivity in Depth一章明确有说:

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

    三、解决办法

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>解决方案:watch、computed</title>
    </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的要点

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

    大概梳理一下:

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

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

    3.可以变相操作props

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

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

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

     
  • 相关阅读:
    全站仪定向距离差 方向不差 这样敢放线吗
    关于老王
    cad巧用插件自定义填充图形
    老王教你永不会错的测量坐标方位角计算方法
    jqgrid 点击列头的超链接或按钮时,不触发列排序事件
    jqgrid 将列头设置为超链接或按钮
    jqgrid 设置隔行换色
    jqgrid 设置行编辑为本地端编辑状态
    jqgrid 让隐藏的列在编辑状态时出现且可编辑
    jqgrid 设置编辑行中的某列为下拉选择项
  • 原文地址:https://www.cnblogs.com/mouseleo/p/11135291.html
Copyright © 2011-2022 走看看