如何追蹤變化闰非?
把一個(gè)js對(duì)象傳遞給Vue實(shí)例的data選項(xiàng),Vue將遍歷此對(duì)象的所有屬性峭范,并通過(guò)<pre><code>Object.defineProperty</code></pre>把這些屬性轉(zhuǎn)化為setter/getter财松。也就是在實(shí)例初始化的過(guò)程中,Vue的內(nèi)部方法_proxy遍歷data中屬性的key纱控,并通過(guò)<pre><code>Object.defineProperty</code></pre>把這些屬性轉(zhuǎn)化為setter/getter辆毡,將data中的屬性代理到vm實(shí)例上。
用戶看不到getter/setter,但是在內(nèi)部Vue通過(guò)它們追蹤依賴甜害,在屬性被訪問(wèn)和修改的時(shí)候通知變化舶掖。
每個(gè)組件實(shí)例都有相應(yīng)的watcher實(shí)例對(duì)象,它在組件渲染過(guò)程中把屬性記錄為依賴尔店。之后當(dāng)依賴項(xiàng)的setter被調(diào)用時(shí)眨攘,會(huì)通知watcher重新計(jì)算主慰,從而使它關(guān)聯(lián)的組件得以跟新。
不在data上的屬性鲫售,Vue是沒(méi)有辦法檢測(cè)的.Vue不允許在已經(jīng)創(chuàng)建的實(shí)例上動(dòng)態(tài)添加根級(jí)響應(yīng)式屬性共螺,但是可以通過(guò)<pre><code>Vue.set(object, key ,value)</code></pre>方法將響應(yīng)屬性添加到嵌套的對(duì)象上。也可以使用vm.$set實(shí)例方法情竹,是全局Vue.set方法的別名藐不。 <pre><code>this.$set(this.object, key, value)</code></pre>
例子:
var app1 = new Vue({ el:'#example', data:{ test:{ } } }); app1.$set(app1.test, 'b', 2); console.log(app1.test); //是一個(gè)Object,并且值是b:2
但是下面的寫法不能得到期望效果:
app1.$set(app1.$data, 'b', 2); //undefined
因?yàn)椴辉试S動(dòng)態(tài)添加根級(jí)屬性鲤妥。
還可以使用Object.assign()擴(kuò)展原對(duì)象的屬性佳吞。
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
例子:
app1.$set(app1.test, 'b', 2); app1.test = Object.assign({}, app1.test, {'a':1, 'c':3}); console.log(app1.test); //Object的值有:a:1, b:2, c:3
知識(shí)擴(kuò)展:
VUEAngular都實(shí)現(xiàn)了雙向數(shù)據(jù)綁定拱雏,機(jī)制有不同棉安。
angular的雙向綁定是使用臟檢查機(jī)制實(shí)現(xiàn)的。
實(shí)現(xiàn)機(jī)制如下:
對(duì)于有可能引起數(shù)據(jù)變化的事件進(jìn)行封裝,每次這些事件被觸發(fā)時(shí)候,就對(duì)數(shù)據(jù)進(jìn)行一遍檢查如蚜,如果發(fā)現(xiàn)數(shù)據(jù)有所變化我衬,就進(jìn)行相應(yīng)的更新回調(diào)。但是某個(gè)事件觸發(fā)時(shí)候碳锈,需要檢查哪個(gè)數(shù)據(jù)呢?我們并不知道這個(gè)事件有可能導(dǎo)致變化的對(duì)應(yīng)數(shù)據(jù),因此要粗暴的將所有數(shù)據(jù)進(jìn)行完全檢查滥嘴。但是有時(shí)候某個(gè)數(shù)據(jù)改變了,并不止影響自己至耻,也有可能會(huì)影響到其他數(shù)據(jù)若皱。因此,如果一次完全檢查視作一個(gè)檢查周期尘颓,那我們至少要進(jìn)行兩個(gè)周期走触。第二個(gè)周期用來(lái)確認(rèn)前一遍的數(shù)據(jù)變動(dòng)中,是否引起了其他的數(shù)據(jù)變動(dòng)疤苹。如果第二遍周期也有變動(dòng)互广,那么就需要繼續(xù)進(jìn)行第三次檢查周期,直到兩次檢查的數(shù)據(jù)完全一致則停止卧土。(來(lái)自同學(xué)分享)
VUE數(shù)據(jù)劫持方式的核心是依賴與Object.defineProperty()惫皱。不得不說(shuō)這是個(gè)了不起的方法,就是它支撐起了vue的雙向綁定尤莺。
Object.defineProperty()可以利用getter和setter對(duì)對(duì)象的各個(gè)屬性進(jìn)行劫持旅敷,一旦set值,則數(shù)據(jù)變動(dòng)缝裁,此時(shí)通知觀察者扫皱,進(jìn)行回調(diào)更新足绅。
這種方式的核心組件有四點(diǎn):
- Observer :數(shù)據(jù)監(jiān)聽(tīng)器,對(duì)接數(shù)據(jù)韩脑,能夠?qū)?shù)據(jù)對(duì)象的所有屬性進(jìn)行監(jiān)聽(tīng)氢妈,如果監(jiān)聽(tīng)到數(shù)據(jù)有變動(dòng),可拿到最新值并通知訂閱者
- Compile:指令解析器段多,對(duì)接dom首量,對(duì)每個(gè)元素節(jié)點(diǎn)的指令進(jìn)行掃描和解析,根據(jù)指令模板替換數(shù)據(jù)进苍,以及綁定相應(yīng)的更新函數(shù)
- Watcher:連接Observer和Compile的橋梁加缘,能夠訂閱并收到每個(gè)屬性變動(dòng)的通知,執(zhí)行指令綁定的相應(yīng)回調(diào)函數(shù)觉啊,從而更新視圖拣宏。
- MVVM:入口函數(shù),整合以上三者
異步更新隊(duì)列:
首先要了解什么是事件循環(huán)杠人?
JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop 這篇文章寫的很清晰勋乾。
可能你還沒(méi)有注意到,Vue異步執(zhí)行 DOM 更新嗡善。只要觀察到數(shù)據(jù)變化辑莫,Vue 將開啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變罩引。如果同一個(gè) watcher 被多次觸發(fā)各吨,只會(huì)一次推入到隊(duì)列中。這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對(duì)于避免不必要的計(jì)算和 DOM 操作上非常重要袁铐。然后揭蜒,在下一個(gè)的事件循環(huán)“tick”中,Vue 刷新隊(duì)列并執(zhí)行實(shí)際(已去重的)工作昭躺。Vue 在內(nèi)部嘗試對(duì)異步隊(duì)列使用原生的Promise.then和MutationObserver忌锯,如果執(zhí)行環(huán)境不支持,會(huì)采用setTimeout(fn, 0)代替领炫。
例如偶垮,當(dāng)你設(shè)置vm.someData = 'new value',該組件不會(huì)立即重新渲染帝洪。當(dāng)刷新隊(duì)列時(shí)似舵,組件會(huì)在事件循環(huán)隊(duì)列清空時(shí)的下一個(gè)“tick”更新。多數(shù)情況我們不需要關(guān)心這個(gè)過(guò)程葱峡,但是如果你想在 DOM 狀態(tài)更新后做點(diǎn)什么砚哗,這就可能會(huì)有些棘手。雖然 Vue.js 通常鼓勵(lì)開發(fā)人員沿著“數(shù)據(jù)驅(qū)動(dòng)”的方式思考砰奕,避免直接接觸 DOM蛛芥,但是有時(shí)我們確實(shí)要這么做提鸟。為了在數(shù)據(jù)變化之后等待 Vue 完成更新 DOM ,可以在數(shù)據(jù)變化之后立即使用Vue.nextTick(callback)仅淑。這樣回調(diào)函數(shù)在 DOM 更新完成后就會(huì)調(diào)用称勋。例如:
{{message}} var vm =new Vue({ el:'#example', data: { message:'123' } }) vm.message ='new message'// 更改數(shù)據(jù) vm.$el.textContent ==='new message'// false Vue.nextTick(function(){ vm.$el.textContent ==='new message'// true })