PHP8.1.21版本已发布
vue8.1.21版本已发布
jquery8.1.21版本已发布

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

不言
不言 转载
2018-11-27 14:54:24 4345浏览

本篇文章给大家带来的内容是关于vue中用props给data赋初始值时遇到的问题及解决方法,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

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

一、现象

代码:

nbsp;html>


    <meta>
    <title>用props初始化data中变量</title>
    <script></script><div>
    <user-info></user-info></div>
<script>
    //全局组件
    let userInfo = Vue.component(&#39;userInfo&#39; ,{
        name: &#39;user-info&#39;,
        props: {
            userData: Object
        },
        data() {
          return {
              userName: this.userData.name
          }
        },
        template: `
            <div>
                <div>姓名:{{userName}}
                <div>性别:{{userData.gender}}
                <div>生日:{{userData.birthday}}
            
        `
    });

    //Vue实例
    new Vue({
        el: &#39;#app&#39;,
        data: {
            user: {
                name: &#39;&#39;,
                gender: &#39;&#39;,
                birthday: &#39;&#39;
            }
        },
        created(){
           this.getUserData();
        },
        methods:{
            getUserData(){
                setTimeout(()=>{
                    this.user = {
                        name: &#39;于永雨&#39;,
                        gender: &#39;男&#39;,
                        birthday: &#39;1991-7&#39;
                    }
                }, 500)
            }
        },
        components: {
            userInfo
        }
    });
</script>

代码解读:

  1. 根组件data中有一个对象:user,包含三个属性:name、gender、birthday,初始值都为空字符串

  2. 模拟api异步请求,500毫秒后对user的重新赋值,三个属性都不再为空

  3. 声明一个子组件userInfo,props中有一个对象userData,用于接收父组件的user;data中有一个变量userName,初始值来自于userData.name

结果:

868320973-5bf9e66f0cf35_articlex.jpg

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

为什么会这样呢?

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

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

nbsp;html>


    <meta>
    <title>用props初始化data中变量-对象形式</title>
    <script></script><div>
    <user-info></user-info></div>
<script>
    //全局组件
    let userInfo = Vue.component(&#39;userInfo&#39; ,{
        name: &#39;user-info&#39;,
        props: {
            userData: Object
        },
        data() {
          return {
              userName: this.userData.name
          }
        },
        template: `
            <div>
                <div>姓名:{{userName.text}}
                <div>性别:{{userData.gender}}
                <div>生日:{{userData.birthday}}
            
        `
    });


    //Vue实例
    new Vue({
        el: &#39;#app&#39;,
        data: {
            user: {
                name: {text: &#39;&#39;},
                gender: &#39;&#39;,
                birthday: &#39;&#39;
            }
        },
        created(){
           this.getUserData();
        },
        methods:{
            getUserData(){
                setTimeout(()=>{
                    this.user = {
                        name: {text: &#39;于永雨&#39;},
                        gender: &#39;男&#39;,
                        birthday: &#39;1991-7&#39;
                    }
                }, 500)
            }
        },
        components: {
            userInfo
        }
    });
</script>

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

二、原因

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

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

434540271-5bfbb5132e9c1_articlex.png

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

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

2191340175-5bfbb8747f671_articlex.png

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

三、解决办法

既然因为data深拷贝的原因,data无法随着props的变化而更新,我们很自然的就想到Vue中有监听作用的两个功能:watch、computed
修改代码如下,观察结果:

nbsp;html>


    <meta>
    <title>解决方案:watch、computed</title>
    <script></script><div>
    <user-info></user-info></div>
<script>
    //全局组件
    let userInfo = Vue.component(&#39;userInfo&#39; ,{
        name: &#39;user-info&#39;,
        props: {
            userData: Object
        },
        data() {
          return {
            userName: this.userData.name
          }
        },
        computed: {
            computedUserName(){
                return this.userData.name
            }
        },
        watch: {
            &#39;userData.name&#39;: function (val) {//监听props中的属性
                this.userName = val;
            }
        },
        template: `
            <div>
                <div>姓名(watch):{{ userName }}
                <div>姓名(computed):{{ computedUserName }}
                <div>性别:{{ userData.gender }}
                <div>生日:{{ userData.birthday }}
            
        `
    });


    //Vue实例
    new Vue({
        el: &#39;#app&#39;,
        data: {
            user: {
                name: &#39;&#39;,
                gender: &#39;&#39;,
                birthday: &#39;&#39;
            }
        },
        created(){
           this.getUserData();
        },
        methods:{
            getUserData(){
                setTimeout(()=>{
                    this.user = {
                        name: &#39;于永雨&#39;,
                        gender: &#39;男&#39;,
                        birthday: &#39;1991-7&#39;
                    }
                }, 500)
            }
        },
        components: {
            userInfo
        }
    });
</script>

运行结果

1450334126-5bfbc2256a927_articlex.png

完美!!!

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

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

246091987-5bfbbb263f05f_articlex.png

大概梳理一下:

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

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

3.可以变相操作props

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

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

声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除