vue項(xiàng)目中修改數(shù)據(jù)屬性,視圖不更新的解決方法

vue.js是以數(shù)據(jù)驅(qū)動(dòng)和組件化的思想構(gòu)建的,相比于其他庫,Vue.js提供了更加簡潔、更易于理解的API,使得我們能夠快上手,所以獲得更多開發(fā)者的青睞,但也踩了不少的坑~~

vue雙向數(shù)據(jù)綁定原理

首先,我們先看官方是怎么解釋vue雙向數(shù)據(jù)綁定的

當(dāng)你把一個(gè)普通的 JavaScript 對象傳入 Vue 實(shí)例作為 data 選項(xiàng),Vue 將遍歷此對象所有的屬性,并使用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setterObject.defineProperty 是 ES5 中一個(gè)無法 shim 的特性,這也就是 Vue 不支持 IE8 以及更低版本瀏覽器的原因。

這些 getter/setter 對用戶來說是不可見的,但是在內(nèi)部它們讓 Vue 能夠追蹤依賴,在屬性被訪問和修改時(shí)通知變更。這里需要注意的是不同瀏覽器在控制臺打印數(shù)據(jù)對象時(shí)對 getter/setter 的格式化并不同,所以建議安裝 vue-devtools 來獲取對檢查數(shù)據(jù)更加友好的用戶界面。

每個(gè)組件實(shí)例都對應(yīng)一個(gè) watcher 實(shí)例,它會(huì)在組件渲染的過程中把“接觸”過的數(shù)據(jù)屬性記錄為依賴。之后當(dāng)依賴項(xiàng)的 setter 觸發(fā)時(shí),會(huì)通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染。

我們可以看出vue的雙向數(shù)據(jù)綁定與Aangular雙向數(shù)據(jù)綁定不同,data中的屬性必須在vue實(shí)例化之前就該存在,因?yàn)関ue實(shí)例化時(shí)對屬性執(zhí)行g(shù)etter/setter處理,才能正確的進(jìn)行轉(zhuǎn)換,所以vue不能檢測到實(shí)例化后data屬性的添加和修改。

Vue.nextTick([callback,context])

在一個(gè)組件實(shí)例中,只有在data里初始化的數(shù)據(jù)才是響應(yīng)的,vue不能檢測到對象屬性的添加或刪除,沒有在data里聲明的屬性不是響應(yīng)的
一個(gè)回調(diào)函數(shù),怎么回事呢?原來是在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào),也就是說在修改數(shù)據(jù)屬性之后調(diào)用這個(gè)函數(shù),能獲取到更新好的DOM。開始做點(diǎn)小測試

模板

<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);
            }
        }
    }

運(yùn)行前



運(yùn)行后


message.b渲染為message.a的內(nèi)容,而message.c的內(nèi)容還是原始的數(shù)據(jù),說明Vue中DOM更新是異步的

官方文檔給出的解釋是

Vue 異步執(zhí)行 DOM 更新。只要觀察到數(shù)據(jù)變化,Vue 將開啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變。如果同一個(gè) watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對于避免不必要的計(jì)算和 DOM 操作上非常重要。然后,在下一個(gè)的事件循環(huán)“tick”中,Vue 刷新隊(duì)列并執(zhí)行實(shí)際 (已去重的) 工作。Vue 在內(nèi)部嘗試對異步隊(duì)列使用原生的 Promise.then 和MessageChannel,如果執(zhí)行環(huán)境不支持,會(huì)采用 setTimeout(fn, 0)代替。

Vue.nextTick用于延遲執(zhí)行一段代碼,它接受2個(gè)參數(shù)(回調(diào)函數(shù)和執(zhí)行回調(diào)函數(shù)的上下文環(huán)境),如果沒有提供回調(diào)函數(shù),那么將返回promise對象。

因此,在Vue生命周期的created()鉤子函數(shù)進(jìn)行的DOM操作一定要放在Vue.nextTick()的回調(diào)函數(shù)中,我們都知道,mounted()鉤子函數(shù)執(zhí)行時(shí)所有的DOM掛載和渲染都已完成,在此函數(shù)中進(jìn)行任何DOM操作都不會(huì)有問題

Vue.set()

向響應(yīng)式對象中添加一個(gè)屬性,并確保這個(gè)新屬性同樣是響應(yīng)式的,且觸發(fā)視圖更新。它必須用于向響應(yīng)式對象上添加新屬性,因?yàn)?Vue 無法探測普通的新增屬性 ,前面提到,只有在data里初始化的數(shù)據(jù)才是響應(yīng)的
官方文檔中特別強(qiáng)調(diào)

注意對象不能是Vue實(shí)例,或者Vue實(shí)例的根數(shù)據(jù)對象。

什么意思呢?就是Vue不允許在已經(jīng)實(shí)例化的組件上添加新的動(dòng)態(tài)根級響應(yīng)屬性(即直接掛載在data下的屬性),但是可以使用$set方法將相應(yīng)屬性添加到嵌套的對象上

將上面的代碼稍作修改
模板

<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 = "我手一杯 品嘗你的美";
            }
        }
    }

運(yùn)行后結(jié)果



可以看到,message確實(shí)改變了,但是頁面沒有變化,再用vue.set(),模板不變,修改代碼如下

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

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

運(yùn)行后



Vue.set(object,key,value)方法一次只能添加一個(gè)屬性,如果需要向嵌套對象上添加多個(gè)屬性,可以用Object.assign或_.extend(),但是需要?jiǎng)?chuàng)建同時(shí)包含原屬性、新屬性的對象,從而有效觸發(fā)watch()方法

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

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

結(jié)果


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容