vue知識(shí)點(diǎn)-1--變化偵測(cè)-依賴收集

本文相當(dāng)于筆記,最近在看深入淺出vue.js的記錄,需要配合書一起看,如果你也在看這本書可以一起看看這篇文章,不然會(huì)看不懂。
vue 在運(yùn)行是內(nèi)部的狀態(tài)不斷改變,頁(yè)面不停的重新渲染,想要達(dá)到這樣的變化偵測(cè)效果第一步就要解決依賴收集的部分。
依賴收集就是記錄哪些數(shù)據(jù)用到了哪些地方,這樣在變化的時(shí)候就能去精準(zhǔn)的通知其去改變。

大體思路就是用Object.defineProperty 設(shè)置get、set監(jiān)聽到數(shù)據(jù)的讀取和改變,在get中收集依賴,在set通知收集到的依賴去改變。

Dep收集依賴的地方,可以叫做存儲(chǔ)位置

封裝一個(gè)Dep類管理依賴收集的地方(depend依賴 的縮寫,為每個(gè)屬性都new 一個(gè)Dep類,每個(gè)屬性都有自己對(duì)于的依賴收集的地方)
Dep類中 主要的結(jié)構(gòu)
this.subs = [] (subscribe 訂閱 縮寫)這個(gè)數(shù)組就是用來(lái)收集具體哪里調(diào)用的該屬性,
depend()函數(shù) push進(jìn)subs中 就是添加依賴的,判斷window.target是否有值 如果有假加入到subs中(window.target)是全局變量
notify()函數(shù) 用于更新所有的依賴 里面就是一個(gè)循環(huán)調(diào)用subs的update方法

defineReactive()收集和觸發(fā)依賴動(dòng)作

defineReactive() 是個(gè)方法 里面封裝了 Object.defineProperty 統(tǒng)一設(shè)置了get,set為每一個(gè)屬性,get中收集, set中觸發(fā)更新

先是調(diào)用了observe() 傳遞進(jìn)入val
let childOb = observe(val)
這個(gè)函數(shù)內(nèi)部判斷了是否是對(duì)象如果是不是對(duì)象就返回空(數(shù)組也是對(duì)象哦,typeof)
不是對(duì)象,有個(gè)過(guò)濾的機(jī)制避免重復(fù)偵測(cè),根據(jù)__ob__
過(guò)濾后調(diào)用真正的Observe 該類主要功能是循環(huán)對(duì)象遞歸調(diào)用defineReactive使其每個(gè)屬性都具備get,set也就是有收集和觸發(fā)的功能,Observe是個(gè)類 childOb拿到的是他的實(shí)例
Observe稍后介紹繼續(xù)說(shuō)defineReactive

實(shí)例化了let dep = new Dep()

在get中調(diào)用了 dep.depend() 以及 childOb.dep.depend()
這么寫是為了統(tǒng)一在get中進(jìn)行依賴收集 childOb 是用來(lái)處理 數(shù)組的依賴收集,
數(shù)組和對(duì)象的依賴收集都是一樣的 list:[] 要讀取這個(gè)數(shù)組 就要先找到list 肯定會(huì)觸發(fā)list的get
數(shù)組的操作方法,push shift 這些改變數(shù)組的方法不能觸發(fā)getter/setter,數(shù)組用的是重寫覆蓋原始的Array原型方法這樣就知道了用戶操作了那些方法,以這種形式來(lái)收集和觸發(fā)依賴,這也被叫做攔截器

set中調(diào)用 dep.notify()

Watcher類 數(shù)據(jù)變化通知的地方 可以看做vue中的$watch

get()方法 這里很厲害,把this 賦值給全局變量 window.target 然后再去讀取對(duì)應(yīng)的數(shù)據(jù),這樣就觸發(fā)了上一步寫的 defineReactive 中設(shè)置好的get get中調(diào)用dep.depend(), depend把window.target 加入到 subs數(shù)組中,用于以后更改時(shí)update,這樣就順利的把前后鏈接了起來(lái)
update()方法 上面說(shuō)的 notify會(huì)循環(huán)調(diào)用數(shù)據(jù)的更新就是這里的update方法,該方法里是$watch時(shí)寫的回調(diào)用call保持上下文關(guān)系執(zhí)行,
所以每次聲明watch的時(shí)候會(huì)在constructor中調(diào)用this.get(),就可以主動(dòng)的把this添加到dep中,很神奇。

Observer()遞歸偵測(cè)所有屬性

主要用于循環(huán)數(shù)據(jù)下面的所有屬性讓每個(gè)屬性都有收集和通知的能力,這其中也處理了數(shù)組的原型覆蓋,

實(shí)例了dep 用于數(shù)據(jù)依賴收集的

給每個(gè)數(shù)據(jù)加上了一個(gè)不可枚舉的__ob__標(biāo)識(shí),這個(gè)標(biāo)識(shí)就是this,主要是兩個(gè)作用,
1、用于過(guò)濾判斷當(dāng)前屬性是否已經(jīng)被偵測(cè)變化,
2、數(shù)組是偵測(cè)變化是改寫原型上的方法,原型上從this直接拿到__ob__就是Observer的實(shí)例,實(shí)例上有dep 當(dāng)觸發(fā)了改寫的push后就能去調(diào)用dep 中的notify()
這里很巧妙,收集是在defineReactive的get中 調(diào)用是在 原型的方法中,Observer相當(dāng)于是中間的位置,上下都能拿到

如果是對(duì)象就循環(huán)遞歸調(diào)用 defineReactive

判斷如果是數(shù)組重新賦值他的__proto__, __proto__指向的是實(shí)例他的原型,也就是一個(gè)對(duì)象,所以生成一個(gè)對(duì)象就好,這個(gè)對(duì)象以Array.prototype作為基礎(chǔ)在上面改寫,要保證數(shù)據(jù)傳進(jìn)來(lái)調(diào)用 Array.prototype 同樣的方法做處理,返回同樣的返回值,然后在這其中加入收集和通知就ok了,
arrayMethods = Object,creare(Array.prototype);
只改寫了push pop shift unshift splice sort reverse 一共七個(gè)可以改變自身的方法,
用defineProperty改寫,在value中 用 apply保持上下文并且把 ...args傳入進(jìn)去 讓其擁有原本的功能并且返回一樣的值,
額外的操作 let ob = this.__ob __;這個(gè)是Observer的實(shí)例,里面有dep,調(diào)用dep.notify()讓其更新,
如果使用的是push unshift splice 這三,他們是可以在數(shù)組中新增元素的,新增的元素要保持可以偵測(cè)變化,同樣是調(diào)用ob上的方法,observeArray

observeArray 循環(huán)傳遞進(jìn)來(lái)的數(shù)組,調(diào)用observe,又回來(lái)了,---是對(duì)象就循環(huán)遞歸調(diào)用 defineReactive ,是數(shù)組覆蓋__proto__

以上這些大致就是變化偵測(cè)的基本實(shí)現(xiàn)

了解了原理也就知道了為什么vue中一些數(shù)據(jù)的變化是不能更新的。
this.list[0] = 1;
observe 數(shù)組中循環(huán)時(shí)對(duì)象通過(guò),否則不會(huì)賦予收集和通知的功能。數(shù)組中是個(gè)值是不能監(jiān)測(cè)變化的是對(duì)象可以。
list.length = 0
vue在處理是根本沒有這個(gè)回調(diào),所以這樣使用時(shí)都不知道數(shù)組變化了。
delete this.obj.name這樣的操作是不能追蹤變化的,因?yàn)閐efineProperty 的set 只能知道數(shù)據(jù)是否被更改不能知道數(shù)據(jù)是否被刪除或是新增。不過(guò)也有配套的api解決這個(gè)問(wèn)題 vue.set , vue.delete

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

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