美文网首页
vue修改对象属性,视图不更新this.$set和Vue.nex

vue修改对象属性,视图不更新this.$set和Vue.nex

作者: 青争小台 | 来源:发表于2019-12-20 18:23 被阅读0次

vue.js是以数据驱动和组件化的思想构建的,相比于其他库,Vue.js提供了更加简洁、更易于理解的API,使得我们能够快上手,所以获得更多开发者的青睐,但也踩了不少的坑~~

vue双向数据绑定原理

首先,我们先看官方是怎么解释vue双向数据绑定的

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setterObject.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在属性被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

我们可以看出vue的双向数据绑定与Aangular双向数据绑定不同,data中的属性必须在vue实例化之前就该存在,因为vue实例化时对属性执行getter/setter处理,才能正确的进行转换,所以vue不能检测到实例化后data属性的添加和修改。

Vue.nextTick([callback,context])

在一个组件实例中,只有在data里初始化的数据才是响应的,vue不能检测到对象属性的添加或删除,没有在data里声明的属性不是响应的
一个回调函数,怎么回事呢?原来是在下次 DOM 更新循环结束之后执行延迟回调,也就是说在修改数据属性之后调用这个函数,能获取到更新好的DOM。开始做点小测试

模板

<template>
    <div id="nexttick_demo">
        <div ref="aa">{{message.a}}</div>
        <div ref="bb">{{message.b}}</div>
        <div ref="cc">{{message.c}}</div>
        <button @click="msgupdate()">更新</button>
    </div>
</template>

代码块

export default{
        name:"nexttick_demo",
        data(){
            return {
                message:{
                    a:"塞纳河畔 左岸的咖啡",
                    b:"",
                    c:""
                }
            }
        },
        created(){

        },
        methods:{
            msgupdate:function(){

                this.message.a = "我手一杯 品尝你的美";
                this.$nextTick(()=>{
                    this.message.b = this.$refs.aa.innerHTML;
                })
                this.message.c = this.$refs.aa.innerHTML;
                console.log(this.message);
            }
        }
    }

运行前

image

运行后

image

message.b渲染为message.a的内容,而message.c的内容还是原始的数据,说明Vue中DOM更新是异步的

官方文档给出的解释是

Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then 和MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0)代替。

Vue.nextTick用于延迟执行一段代码,它接受2个参数(回调函数和执行回调函数的上下文环境),如果没有提供回调函数,那么将返回promise对象。

因此,在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,我们都知道,mounted()钩子函数执行时所有的DOM挂载和渲染都已完成,在此函数中进行任何DOM操作都不会有问题

Vue.set()

向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 ,前面提到,只有在data里初始化的数据才是响应的
官方文档中特别强调

注意对象不能是Vue实例,或者Vue实例的根数据对象。

什么意思呢?就是Vue不允许在已经实例化的组件上添加新的动态根级响应属性(即直接挂载在data下的属性),但是可以使用$set方法将相应属性添加到嵌套的对象上

将上面的代码稍作修改
模板

<template>
    <div id="nexttick_demo">
        <div ref="aa">{{message.a}}</div>
        <div ref="bb">{{message.b}}</div>
        <button @click="msgupdate()">更新</button>
    </div>
</template>

代码块

export default{
        name:"nexttick_demo",
        data(){
            return {
                message:{
                    a:"塞纳河畔 左岸的咖啡"
                }
            }
        },
        created(){

        },
        methods:{
            msgupdate:function(){   
                this.message.b = "我手一杯 品尝你的美";
            }
        }
    }

运行后结果

image

可以看到,message确实改变了,但是页面没有变化,再用vue.set(),模板不变,修改代码如下

export default{
        name:"nexttick_demo",
        data(){
            return {
                message:{
                    a:"塞纳河畔 左岸的咖啡"
                }
            }
        },
        created(){

        },
        methods:{
            msgupdate:function(){   
                this.$set(this.message,'b',"我手一杯 品尝你的美");
                // vm.$set()实例方法是Vue.set()全局方法的别名
            }
        }
    }

运行后

image

Vue.set(object,key,value)方法一次只能添加一个属性,如果需要向嵌套对象上添加多个属性,可以用Object.assign或_.extend(),但是需要创建同时包含原属性、新属性的对象,从而有效触发watch()方法

export default{
        name:"nexttick_demo",
        data(){
            return {
                message:{
                    a:"塞纳河畔 左岸的咖啡"
                }
            }
        },
        created(){

        },
        methods:{
            msgupdate:function(){   
                this.message = Object.assign({},this.message,{b:'我手一杯 品尝你的美',c:'留下唇印的嘴'})
            }
        }
    }

结果

image

this.$set补充

往响应式对象this.$data中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 .

data () {
    return {
        user: {
            name: '',
            id: ''
        }
    }
}

如果直接给user增加属性

this.user.age = 30

虽然user的属性值age的值为30,但是页面上的视图是不发生任何刷新的,只能展示name/id的值,age值还是为空。

原因:由于Vue.js 不能检测到对象属性的添加或删除。因为 Vue.js 在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue.js 转换它,才能让它是响应的。

此时要解决上述问题,给对象添加属性的时候,页面视图跟着刷新,可以用set的方法:

this.$set(this.user, "age", 30)

我在项目中遇到问题是数组的每一项都要动态添加响应式属性,我这样写也是有效果的:

for (let plan of this.formPayment.planList) {
              this.$set(plan, 'socialInsuranceRanges', [{value: '1', label: '有', code: '', price: ''}, {value: '2', label: '无', code: '', price: ''}]);
            }

还有一种情况是给已有对象添加多个属性,可能会用到Object.assgin(),这种情况,应该用两个对象创建一个新对象

// 不要这样写:
Object.assign( this.user, {
  tel: 18888888888,
  sex: 'Y'
})

// 而应该这样写:
this.user = Object.assign( {}, this.user, {
  tel: 18888888888,
  sex: 'Y'
})

修改数组对象的某一项

this.$set(this.arr, index, this.arr[index]);

本文转载自https://www.jianshu.com/p/e1e92965d4fe
https://www.jianshu.com/p/e24292fa6ec7

相关文章

网友评论

      本文标题:vue修改对象属性,视图不更新this.$set和Vue.nex

      本文链接:https://www.haomeiwen.com/subject/brpinctx.html