vue修改对象属性,视图不更新this.$set和Vue.nextTick([callback,context])

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容