Copy自
如何追蹤變化
Vue會遍歷data選項中的所有屬性,并使用Object.defineProperty把這些屬性全部轉(zhuǎn)化為getter/setter。
Object.defineProperty是ES5中一個無法優(yōu)雅降級的特性,這也是Vue不支持IE8及更低版本瀏覽器的原因。
這些getter/setter對用戶來說是不可見的,但是它們讓Vue能夠追蹤依賴,在屬性被訪問和修改時通過getter/setter來通知變更。
每個組件實例都對應(yīng)一個watcher實例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù)屬性記錄為依賴。之后當(dāng)依賴項的setter觸發(fā)時,會通知watcher,從而使它關(guān)聯(lián)的組件重新渲染。
檢測變化的注意事項
受現(xiàn)代JavaScript的限制,Vue無法檢測到對象屬性的添加或刪除。
Vue會在初始化實例時對屬性執(zhí)行getter/setter轉(zhuǎn)化,所以屬性必須一開始就得在data對象上存在才能讓Vue用Object.defineProperty將它轉(zhuǎn)化為響應(yīng)式的。列如:
<div>
<span v-for="(item, index) in obj" :key="index">{{item}}</span>
<button @click="addObj">添加屬性</button> //
</div>
data() {
return {
obj: {
a:1 // 響應(yīng)式屬性
}
}
},
methods: {
addObj() {
this.obj.b = 2 // 非響應(yīng)式屬性
}
},
上面的代碼希望調(diào)用addObj方法向this.obj添加屬性并且視圖能夠更新。失?。⌒枰蝽憫?yīng)式對象添加屬性應(yīng)該用Vue.set()方法才能將添加的屬性變?yōu)轫憫?yīng)式的。
對于已經(jīng)創(chuàng)建的實例,Vue不允許動態(tài)添加根級別的響應(yīng)式屬性。但是,Vue提供了Vue.set(obj, propertyName, value)方法向嵌套對象添加響應(yīng)式屬性。列如:
this.$set(this.obj, 'b', 2)
需要為已有對象賦值多個新屬性時應(yīng)該用原對象與要混合進(jìn)去的對象的新屬性一起創(chuàng)建一個新對象:
this.obj = Object.assign({}, this.obj, {c: 3, d: 4})
異步更新隊列
Vue在更新DOM時是異步執(zhí)行的。只要偵聽到數(shù)據(jù)的變化,Vue將開啟一個隊列,并緩沖 在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更。如果同一個watcher被多次觸發(fā),只會被推入到隊列中一次。這種在緩沖時去除重復(fù)數(shù)據(jù)對于避免不必要的計算和DOM操作非常重要。然后,在下一個事件循環(huán)tick中,Vue刷新隊列并執(zhí)行實際工作。Vue在內(nèi)部對異步隊列嘗試使用原生的Promise.then、MutationObserver和setTimeOut,如果執(zhí)行環(huán)境不支持,則會采用setTimeOut(fn, 0)代替。
為了在數(shù)據(jù)變化之后等待Vue完成更新DOM,可以在數(shù)據(jù)變化后立即使用Vue.nextTick(callback)。這樣回調(diào)函數(shù)將在DOM更新完后被調(diào)用。
nextTick()返回一個Promise對象,所以可以使用async/await語法:
async function () {
<!-- DOM未更新 -->
await this.$nextTick()
<!-- DOM已更新 -->
}