zoukankan      html  css  js  c++  java
  • Vue之数据传递

    基础:vue的响应式规则

    简单的props更新

    父组件

    <template>
        <div>
            <block-a :out-data="x"></block-a>
        </div>
    </template>
    
    <script>
        import blockA from './block-a';
    
        export default {
            name: "App",
            components:{
                blockA
            },
            data(){
                return {
                    x:123
                }
            },
            mounted(){
                setTimeout(()=>{
                    this.x = 789;
                },1000)
            }
        }
    </script>
    View Code

    子组件

    <template>
        <div>
            这是子组件
            {{outData}}
        </div>
    </template>
    
    <script>
        export default {
            name: "block-a",
            props:['outData'],
            watch:{
                outData(newVal){
                    console.log("新的值是:" + newVal)
                }
            }
    
        }
    </script>
    View Code

    运行效果,界面先展示123,一秒后展示789,控制台仅输出了“新的值:789”。

    结论:简单的数值类型能通过props动态反映到子组件内,而且能被子组件watch检测。

    props对象的更新

    父组件

    <template>
        <div>
            <block-a :out-data="x"></block-a>
        </div>
    </template>
    
    <script>
        import blockA from './block-a';
    
        export default {
            name: "App",
            components:{
                blockA
            },
            data(){
                return {
                    x:{
                        a:123,
                        b:999
                    }
                }
            },
            mounted(){
                setTimeout(()=>{
                    this.x.a = 789;
                },1000)
            }
        }
    </script>
    View Code

    子组件

    <template>
        <div>
            这是子组件
            {{outData}}
        </div>
    </template>
    
    <script>
        export default {
            name: "block-a",
            props:['outData'],
            watch:{
                outData(newVal){
                    console.log("新的值是:" + newVal)
                }
            }
        }
    </script>
    View Code

    这个例子对比上一个例子,将传递的数据改为了对象类型。对对象属性的变更,能动态反映到子组件中(子组件的界面一秒后发生正确的变化),但watch函数却没有执行(控制台无输出)。

    为了监听对象属性的变化,将子组件中的watch设置修改如下:

            watch:{
                outData:{
                    handler(newVal){
                        console.log("新的值是:" + newVal)
                    },
                    deep:true
                }
            }
    View Code

    这样代码运行起来,不仅界面会发生变化,而且控制台也会有输出了(属性变化能被子组件watch检测到了),虽然检测出来有属性变更,却没办法知道到底是哪个属性发生了变更。

    补充测试1:修改原有属性,同时添加新的属性(this.x.c=123),则子组件中检测出对象原有属性发生变更后,读取到的对象中也包含了新的属性(c=123)。

    补充测试2:父组件中直接为属性赋值一个新的对象,如

    setTimeout(()=>{
        this.x = {
            kkk:123
        };
    },1000)
    View Code

    则,不管子组件中采用深层watch还是普通的watch检测,都检测出变更(控制台有输出),而且界面也会发生正确变化。

    补充测试3:将父组件中的响应式对象删除

    <template>
        <div>
            <block-a :out-data="x"></block-a>
        </div>
    </template>
    
    <script>
        import blockA from './block-a';
    
        export default {
            name: "App",
            components:{
                blockA
            },
            data(){
                return {
    
                }
            },
            mounted(){
                setTimeout(()=>{
                    this.x = {
                        kkk:123
                    };
                },1000)
            }
        }
    </script>
    View Code

    则代码运行会报错,提示渲染期间使用了x,而x未定义。而且后续子组件中没有发生任何变化,控制台也无输出。

    结论:当父组件中的响应式对象通过props与子组件中的属性建立了关联后,父组件中的响应式对象发生变更(原属性发生变化和对象被重新赋值,前提是对应的setter存在,这样的赋值才是响应式的),都会通知到被关联子组件去重新渲染(与这个对象相关的属性部分dom渲染),而且watch函数会进入判断(如果是深层watch,则对比对象的属性,如果是普通watch,则简单对应对象的引用),如果没有判断出来发生变化,则不执行watch回调函数。

    props对象属性的更新

    以上发现一个问题,子组件能监听出对象属性发生了变化,却不知道变化了哪个属性。解决办法是使用简单watch,但是写法得改变一下,要直接定位到我关心的属性上,将子组件的watch修改如下

    watch:{
        ['outData.a'](newVal){
            console.log("新的值是:" + newVal)
        }
    }
    View Code

    对于父组件中的x被重新赋值:新对象不存在a属性或者a属性的值与原来的值不一致时,子组件中的watch才会执行,如果重新赋值后,新对象的a属性与原对象的a属性值相同,则watch回调不执行。

    结论:在上一条结论的基础上,多加一点,这个例子中的这种watch写法,仅仅是简单地再次读取对象的某个属性值(a),与原来记录的值进行对比,如果不一致,则执行watch回调。而不在乎外部的对象是否被重新赋值了,还是属性被修改了。

    修改props

    父组件

    <template>
        <div>
            <block-a :out-data="x"></block-a>
        </div>
    </template>
    
    <script>
        import blockA from './block-a';
    
        export default {
            name: "App",
            components:{
                blockA
            },
            data(){
                return {
                    x:{
                        a:123,
                        b:999
                    }
                }
            },
            mounted(){
                setTimeout(()=>{
                    console.log("父组件中输出")
                    console.log(this.x)
                },2000)
            }
        }
    </script>
    View Code

    子组件

    <template>
        <div>
            这是子组件
            {{outData}}
        </div>
    </template>
    
    <script>
        export default {
            name: "block-a",
            props:['outData'],
            watch:{
                ['outData.a'](newVal){
                    console.log("新的值是:" + newVal)
                }
            },
            mounted(){
                this.outData.a = 888;
                setTimeout(()=>{
                    console.log("子组件中输出")
                    console.log(this.outData)
                },1000)
            }
        }
    </script>
    View Code

    运行效果:子组件内的watch回调执行,然后子组件内输出了修改后的对象,接着父组件也输出了修改后的对象(a=888)。

    虽然props传递(文档中解释props是单向传递的),但因为传递的是对象,所以子组件内对对象的修改等同于父组件中对对象的修改。子组件内通过props获取到的对象和父组件中的对象是一样的,打印出来,发现对象属性上都有getter和setter。

    将对象类型改为普通类型,然后在子组件中对props进行修改,会有如下警告:

    [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "outData"

    虽然有这样的警告,却不会影响代码的执行(与出异常不一样,修改语句后面的代码依然会继续执行),子组件内props的变化依然是响应式的(界面显示也会跟着变化),但这样的修改却不会反映到父组件中,父组件中的输出依然是旧的值。

    props的转换

    上一个例子提示:不能直接修改props值,而应该使用基于props的data或computed属性。这里来演示一下。

    父组件

    <template>
        <div>
            <block-a :out-data="x"></block-a>
        </div>
    </template>
    
    <script>
        import blockA from './block-a';
    
        export default {
            name: "App",
            components:{
                blockA
            },
            data(){
                return {
                    x:{
                        a:123,
                        b:999
                    }
                }
            },
            mounted(){
                setTimeout(()=>{
                    console.log("父组件中输出")
                    console.log(this.x)
                },2000)
            }
        }
    </script>
    View Code

    子组件中将props赋值给data属性

    <template>
        <div>
            这是子组件
            {{outData}}
        </div>
    </template>
    
    <script>
        export default {
            name: "block-a",
            props:['outData'],
            watch:{
                ['outData.a'](newVal){
                    console.log("新的值是:" + newVal)
                }
            },
            data(){
                return {
                    localData:this.outData,
                }
            },
            mounted(){
                this.localData.a = 888;
                setTimeout(()=>{
                    console.log("子组件中输出")
                    console.log(this.localData)
                },1000)
            }
        }
    </script>
    View Code

    代码运行的效果:与直接修改props对象效果一致,因为虽然修改的是本地的data属性对象,而它指向的是与props一样的对象,所以执行起来,子组件的watch函数会输出,而且子组件和父组件中的定时器输出是相同的对象。

    父组件将传递的值从对象类型修改为基本类型,将子组件代码修改如下

    <template>
        <div>
            这是子组件
            {{localData}}
        </div>
    </template>
    
    <script>
        export default {
            name: "block-a",
            props:['outData'],
            watch:{
                ['outData.a'](newVal){
                    console.log("新的值是:" + newVal)
                }
            },
            data(){
                return {
                    localData:this.outData,
                }
            },
            mounted(){
                this.localData = 888;
                setTimeout(()=>{
                    console.log("子组件中输出")
                    console.log(this.localData)
                },1000)
            }
        }
    </script>
    View Code

    将基本类型从props属性转为data属性后,修改data属性,而不修改props属性,就不会提示之前的警告了,而且也不会影响父组件中的值。

    但是这样修改之后,当父组件修改了传入的数据,因为子组件中直接读取的是data属性,而不是props属性,所以子组件没办法感知props的变更,解决办法是:在子组件中添加一个对props的watch,回调函数中更新data的值。接下来演示一下父子组件交替修改值的示例。

    父组件

    <template>
        <div>
            <block-a :out-data="x"></block-a>
        </div>
    </template>
    
    <script>
        import blockA from './block-a';
    
        export default {
            name: "App",
            components:{
                blockA
            },
            data(){
                return {
                    x:"123"
                }
            },
            mounted(){
                let i = 0;
                setInterval(()=>{
                    this.x = "父组件中设置新值:" + i++;
                },5000,100)
            }
        }
    </script>
    View Code

    子组件

    <template>
        <div>
            这是子组件
            {{localData}}
        </div>
    </template>
    
    <script>
        export default {
            name: "block-a",
            props:['outData'],
            watch:{
                ['outData'](newVal){
                    this.localData = newVal;
                }
            },
            data(){
                return {
                    localData:this.outData,
                }
            },
            mounted(){
                let i = 0;
                setInterval(()=>{
                    this.localData = "子组件中设置新值:" + i++;
                },1000)
            }
        }
    </script>
    View Code

    运行效果:子组件既能接收到父组件中的值,也能使用自己本地的值。

    可以发现,才用这种“监听props + props转data”的办法,组件内的data数据来源就有两种了:来源于当前组件 + 来源于父组件。这种特点可以实现一个带缓存的分页组件,简单描述如下:

    组件内维护一个data属性dataList,用于存储要展示的数据,当有组件内有数据缓存时,则将缓存赋值给dataList,来展示缓存的数据,当没有缓存时,则读取外部的props传入的值来显示,通过将这个新的数据缓存起来【这一步读取props的值时需要注意props变化的异步性】。

    ps:实现props到data的双向绑定,可以查看这个代码 https://github.com/xxcanghai/cnblogsFiles/blob/master/vue-mixins/propsync.js

    props变化的异步性

    父组件中修改数据后,子组件中立马读取到的props值,并不是父组件中修改后的值。演示如下:

    父组件

    <template>
        <div>
            <block-a ref="subBlock" :out-data="x"></block-a>
        </div>
    </template>
    
    <script>
        import blockA from './block-a';
    
        export default {
            name: "App",
            components:{
                blockA
            },
            data(){
                return {
                    x:"123"
                }
            },
            mounted(){
                this.x = 888;
                this.$refs.subBlock.getProps(); // 123
            }
        }
    </script>
    View Code

    子组件

    <template>
        <div>
            这是子组件
            {{outData}}
        </div>
    </template>
    
    <script>
        export default {
            name: "block-a",
            props: ['outData'],
            methods: {
                getProps() {
                    console.log("获取到的props是:" + this.outData)
                }
            }
        }
    </script>
    View Code

    运行效果:控制台输出123,而界面显示888 。这是因为JS是单线程的,父组件中修改完后,马上调用子组件的方法读取props值,这时响应式的代码还没执行,而应该将读取响应式的变化放到下一次vue循环中读取即可,子组件代码修改如下

    getProps() {
        this.$nextTick(()=>{
            console.log("获取到的props是:" + this.outData)
        })
    }
    View Code

    这样控制台也输出888了。

    数组对象的props更新

    还没测试,但应该与对象的情况一致,只不过数组与对象的响应式变化有些区别而已【注意有些情况下数组没办法去检测变化】

  • 相关阅读:
    Erlang学习记录:转义
    Erlang学习记录:运算符
    Erlang学习记录:语法和特性
    Erlang学习记录:相关工具和文档
    IDEA快捷键(收集自网络后整理)
    Redis学习
    SQL_server_2008_r2和visual studio 2010旗舰版的安装(2013-01-16-bd 写的日志迁移
    oracle 11gR2 for win7旗舰版64安装以及连接plsql和NaviCat(win64_11gR2_database) (2012-12-31-bd 写的日志迁移
    win7在某个盘或文件夹中出现右键只能新建文件夹的情况 (2012-12-28-bd 写的日志迁移
    宏基笔记本升级bios(2012-12-28-bd 写的日志迁移
  • 原文地址:https://www.cnblogs.com/hellohello/p/10217394.html
Copyright © 2011-2022 走看看