您好,欢迎访问一九零五行业门户网

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

本篇文章给大家带来的内容是关于vue中用props给data赋初始值时遇到的问题及解决方法,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
前段时间做一个运营活动的项目,上线后产品反馈页面埋点不对,在排查过程中发现,问题竟然是由于vue中的data初始值导致,而data的初始值来自于props。为方便描述,现将问题抽象如下:
一、现象
代码:
<!doctype html><html><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
结果:
页面初始化后,姓名、性别、生日都显示为空,500毫秒后性别和生日显示正常结果,仅姓名没有变化。
为什么会这样呢?
我最初的想法:user.name是string,属于基本数据类型,用它给子组件data中username赋值,属于基本数据类型赋值,所以当父组件中user.name变化时,子组件中username并不会随之变化。
是这样的吗?于是我决定将user.name改为对象,通过引用数据类型赋值,然后观察是否符合预期。代码如下:
<!doctype html><html><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初始化时会递归地遍历data所有的属性,并使用object.defineproperty把这些属性全部转为getter/setter,用于实现双向绑定。官方文档在reactivity in depth一章明确有说:
还顺便解释了一下为什么vue不支持ie8的原因:ie8不支持object.defineproperty。
三、解决办法既然因为data深拷贝的原因,data无法随着props的变化而更新,我们很自然的就想到vue中有监听作用的两个功能:watch、computed。
修改代码如下,观察结果:
<!doctype html><html><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的要点事后又仔细翻了一下关于props的文档:
大概梳理一下:
1.props是单向数据流:父组件的数据变化,通过props实时反应在子组件中,反之不然2.不允许在子组件中直接操作props3.可以变相操作props(1)在data中声明局部变量,并用props初始化,弊端:局部变量不随着props更新而更新(2)在computed中对props值转换后输出
以上就是vue中用props给data赋初始值时遇到的问题及解决方法的详细内容。
其它类似信息

推荐信息