• 技术文章 >web前端 >js教程

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

    不言不言2018-11-27 14:54:24转载2366
    本篇文章给大家带来的内容是关于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>

    代码解读:

    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改为对象,通过引用数据类型赋值,然后观察是否符合预期。代码如下:

    <!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的文档,其中:

    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
    修改代码如下,观察结果:

    <!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>

    运行结果

    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值转换后输出

    以上就是Vue中用props给data赋初始值时遇到的问题及解决方法的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除
    专题推荐:javascript vue.js
    上一篇:jsp和javascript的区别是什么 下一篇:CORS跨域的深入理解(代码示例)
    大前端线上培训班

    相关文章推荐

    • vue.js怎么获取和解析php传递过来的数组?• vue.js - VueJS怎么与PHP进行数据通信• Vue ES6 Jade Scss Webpack Gulp 快速入门资源• javascript - vuejs如何v-if的用法• vuejs如何v• 请教一下,vue-resource 发送ajax请求始终为nill• Vue.js 跟踪对象新增属性

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网