Vue響應(yīng)式原理Observer、Dep、Watcher理解
開篇
最近在學(xué)習(xí)Vue的源碼,看了網(wǎng)上一些大神的博客,看起來感覺還是蠻吃力的。自己記錄一下學(xué)習(xí)的理解,希望能夠達(dá)到簡(jiǎn)單易懂,不看源碼也能理解的效果😆
Object.defineProperty
相信很多同學(xué)或多或少都了解Vue的響應(yīng)式原理是通過Object.defineProperty實(shí)現(xiàn)的。被Object.defineProperty綁定過的對(duì)象,會(huì)變成「響應(yīng)式」化。也就是改變這個(gè)對(duì)象的時(shí)候會(huì)觸發(fā)get和set事件。進(jìn)而觸發(fā)一些視圖更新。舉個(gè)栗子🌰
function defineReactive (obj, key, val) { Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: () => { console.log('我被讀了,我要不要做點(diǎn)什么好?'); return val; }, set: newVal => { if (val === newVal) { return; } val = newVal; console.log("數(shù)據(jù)被改變了,我要把新的值渲染到頁面上去!"); } }) } let data = { text: 'hello world', }; // 對(duì)data上的text屬性進(jìn)行綁定 defineReactive(data, 'text', data.text); console.log(data.text); // 控制臺(tái)輸出 <我被讀了,我要不要做點(diǎn)什么好?> data.text = 'hello Vue'; // 控制臺(tái)輸出 <hello Vue && 數(shù)據(jù)被改變了,我要把新的值渲染到頁面上去!>
Observer 「響應(yīng)式」
Vue中用Observer類來管理上述響應(yīng)式化Object.defineProperty的過程。我們可以用如下代碼來描述,將this.data也就是我們?cè)赩ue代碼中定義的data屬性全部進(jìn)行「響應(yīng)式」綁定。
class Observer { constructor() { // 響應(yīng)式綁定數(shù)據(jù)通過方法 observe(this.data); } } export function observe (data) { const keys = Object.keys(data); for (let i = 0; i < keys.length; i++) { // 將data中我們定義的每個(gè)屬性進(jìn)行響應(yīng)式綁定 defineReactive(obj, keys[i]); } }
Dep 「依賴管理」
什么是依賴?
相信沒有看過源碼或者剛接觸Dep這個(gè)詞的同學(xué)都會(huì)比較懵。那Dep究竟是用來做什么的呢? 我們通過defineReactive方法將data中的數(shù)據(jù)進(jìn)行響應(yīng)式后,雖然可以監(jiān)聽到數(shù)據(jù)的變化了,那我們?cè)趺刺幚硗ㄖ晥D就更新呢?
Dep就是幫我們收集【究竟要通知到哪里的】。比如下面的代碼案例,我們發(fā)現(xiàn),雖然data中有text和message屬性,但是只有message被渲染到頁面上,至于text無論怎么變化都影響不到視圖的展示,因此我們僅僅對(duì)message進(jìn)行收集即可,可以避免一些無用的工作。
那這個(gè)時(shí)候message的Dep就收集到了一個(gè)依賴,這個(gè)依賴就是用來管理data中message變化的。
<div> <p>{{message}}</p> </div>
data: { text: 'hello world', message: 'hello vue', }
當(dāng)使用watch屬性時(shí),也就是開發(fā)者自定義的監(jiān)聽某個(gè)data中屬性的變化。比如監(jiān)聽message的變化,message變化時(shí)我們就要通知到watch這個(gè)鉤子,讓它去執(zhí)行回調(diào)函數(shù)。
這個(gè)時(shí)候message的Dep就收集到了兩個(gè)依賴,第二個(gè)依賴就是用來管理watch中message變化的。
watch: { message: function (val, oldVal) { console.log('new: %s, old: %s', val, oldVal) }, }
當(dāng)開發(fā)者自定義computed計(jì)算屬性時(shí),如下messageT屬性,是依賴message的變化的。因此message變化時(shí)我們也要通知到computed,讓它去執(zhí)行回調(diào)函數(shù)。 這個(gè)時(shí)候message的Dep就收集到了三個(gè)依賴,這個(gè)依賴就是用來管理computed中message變化的。
computed: { messageT() { return this.message + '!'; } }
圖示如下:一個(gè)屬性可能有多個(gè)依賴,每個(gè)響應(yīng)式數(shù)據(jù)都有一個(gè)Dep來管理它的依賴。
如何收集依賴
我們?nèi)绾沃纃ata中的某個(gè)屬性被使用了,答案就是Object.defineProperty,因?yàn)樽x取某個(gè)屬性就會(huì)觸發(fā)get方法。可以將代碼進(jìn)行如下改造:
function defineReactive (obj, key, val) { let Dep; // 依賴 Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: () => { console.log('我被讀了,我要不要做點(diǎn)什么好?'); // 被讀取了,將這個(gè)依賴收集起來 Dep.depend(); // 本次新增 return val; }, set: newVal => { if (val === newVal) { return; } val = newVal; // 被改變了,通知依賴去更新 Dep.notify(); // 本次新增 console.log("數(shù)據(jù)被改變了,我要把新的值渲染到頁面上去!"); } }) }
什么是依賴
那所謂的依賴究竟是什么呢?上面的圖中已經(jīng)暴露了答案,就是Watcher。
Watcher 「中介」
Watcher就是類似中介的角色,比如message就有三個(gè)中介,當(dāng)message變化,就通知這三個(gè)中介,他們就去執(zhí)行各自需要做的變化。
Watcher能夠控制自己屬于哪個(gè),是data中的屬性的還是watch,或者是computed,Watcher自己有統(tǒng)一的更新入口,只要你通知它,就會(huì)執(zhí)行對(duì)應(yīng)的更新方法。
因此我們可以推測(cè)出,Watcher必須要有的2個(gè)方法。一個(gè)就是通知變化,另一個(gè)就是被收集起來到Dep中去。
class Watcher { addDep() { // 我這個(gè)Watcher要被塞到Dep里去了~~ }, update() { // Dep通知我更新呢~~ }, }
總結(jié)
回顧一下,Vue響應(yīng)式原理的核心就是Observer、Dep、Watcher。
Observer中進(jìn)行響應(yīng)式的綁定,在數(shù)據(jù)被讀的時(shí)候,觸發(fā)get方法,執(zhí)行Dep來收集依賴,也就是收集Watcher。
在數(shù)據(jù)被改的時(shí)候,觸發(fā)set方法,通過對(duì)應(yīng)的所有依賴(Watcher),去執(zhí)行更新。比如watch和computed就執(zhí)行開發(fā)者自定義的回調(diào)方法。
本篇文章屬于入門篇,能夠先簡(jiǎn)單的理解Observer、Dep、Watcher三者的作用和關(guān)系。后面會(huì)逐漸詳細(xì)和深入,循序漸進(jìn)的理解和學(xué)習(xí)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- vue中__ob__:?Observer的踩坑記錄
- Vue之Dep和Observer的用法及說明
- vue3?圖片懶加載的兩種方式、IntersectionObserver和useIntersectionObserver實(shí)例詳解
- 關(guān)于Vue?"__ob__:Observer"屬性的解決方案詳析
- Vue2?Observer實(shí)例dep和閉包中dep區(qū)別詳解
- vue中關(guān)于_ob_:observer的處理方式
- Vue數(shù)組中出現(xiàn)__ob__:Observer無法取值問題的解決方法
- vue中{__ob__: observer}對(duì)象轉(zhuǎn)化為數(shù)組進(jìn)行遍歷方式
相關(guān)文章
在vue2.0中引用element-ui組件庫(kù)的方法
這篇文章主要介紹了在vue2.0中引用element-ui組件庫(kù),需要的朋友可以參考下2018-06-06Vue.js@2.6.10更新內(nèi)置錯(cuò)誤處機(jī)制Fundebug同步支持相應(yīng)錯(cuò)誤監(jiān)控
這篇文章主要介紹了Vue.js@2.6.10更新內(nèi)置錯(cuò)誤處機(jī)制,F(xiàn)undebug同步支持相應(yīng)錯(cuò)誤監(jiān)控 ,需要的朋友可以參考下2019-05-05Vuejs第六篇之Vuejs與form元素實(shí)例解析
本文通過實(shí)例給大家詳細(xì)介紹了Vuejs與form元素的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09vue 實(shí)現(xiàn)基礎(chǔ)組件的自動(dòng)化全局注冊(cè)
這篇文章主要介紹了vue 實(shí)現(xiàn)基礎(chǔ)組件的自動(dòng)化全局注冊(cè)的方法,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下2020-12-12vue 中directive功能的簡(jiǎn)單實(shí)現(xiàn)
本篇介紹directive的簡(jiǎn)單實(shí)現(xiàn),主要學(xué)習(xí)其實(shí)現(xiàn)的思路及代碼的設(shè)計(jì),需要的朋友參考下吧2018-01-01vue實(shí)現(xiàn)滾動(dòng)條到頂部或者到指定位置
這篇文章主要介紹了vue實(shí)現(xiàn)滾動(dòng)條到頂部或者到指定位置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08Element-Ui組件 NavMenu 導(dǎo)航菜單的具體使用
這篇文章主要介紹了Element-Ui組件 NavMenu 導(dǎo)航菜單的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10如何封裝了一個(gè)vue移動(dòng)端下拉加載下一頁數(shù)據(jù)的組件
這篇文章主要介紹了如何封裝了一個(gè)vue移動(dòng)端下拉加載下一頁數(shù)據(jù)的組件,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01