Vue響應式原理Observer、Dep、Watcher理解
開篇
最近在學習Vue的源碼,看了網上一些大神的博客,看起來感覺還是蠻吃力的。自己記錄一下學習的理解,希望能夠達到簡單易懂,不看源碼也能理解的效果😆
Object.defineProperty
相信很多同學或多或少都了解Vue的響應式原理是通過Object.defineProperty實現(xiàn)的。被Object.defineProperty綁定過的對象,會變成「響應式」化。也就是改變這個對象的時候會觸發(fā)get和set事件。進而觸發(fā)一些視圖更新。舉個栗子🌰
function defineReactive (obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
console.log('我被讀了,我要不要做點什么好?');
return val;
},
set: newVal => {
if (val === newVal) {
return;
}
val = newVal;
console.log("數(shù)據(jù)被改變了,我要把新的值渲染到頁面上去!");
}
})
}
let data = {
text: 'hello world',
};
// 對data上的text屬性進行綁定
defineReactive(data, 'text', data.text);
console.log(data.text); // 控制臺輸出 <我被讀了,我要不要做點什么好?>
data.text = 'hello Vue'; // 控制臺輸出 <hello Vue && 數(shù)據(jù)被改變了,我要把新的值渲染到頁面上去!>
Observer 「響應式」
Vue中用Observer類來管理上述響應式化Object.defineProperty的過程。我們可以用如下代碼來描述,將this.data也就是我們在Vue代碼中定義的data屬性全部進行「響應式」綁定。
class Observer {
constructor() {
// 響應式綁定數(shù)據(jù)通過方法
observe(this.data);
}
}
export function observe (data) {
const keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
// 將data中我們定義的每個屬性進行響應式綁定
defineReactive(obj, keys[i]);
}
}
Dep 「依賴管理」
什么是依賴?
相信沒有看過源碼或者剛接觸Dep這個詞的同學都會比較懵。那Dep究竟是用來做什么的呢? 我們通過defineReactive方法將data中的數(shù)據(jù)進行響應式后,雖然可以監(jiān)聽到數(shù)據(jù)的變化了,那我們怎么處理通知視圖就更新呢?
Dep就是幫我們收集【究竟要通知到哪里的】。比如下面的代碼案例,我們發(fā)現(xiàn),雖然data中有text和message屬性,但是只有message被渲染到頁面上,至于text無論怎么變化都影響不到視圖的展示,因此我們僅僅對message進行收集即可,可以避免一些無用的工作。
那這個時候message的Dep就收集到了一個依賴,這個依賴就是用來管理data中message變化的。
<div>
<p>{{message}}</p>
</div>
data: {
text: 'hello world',
message: 'hello vue',
}
當使用watch屬性時,也就是開發(fā)者自定義的監(jiān)聽某個data中屬性的變化。比如監(jiān)聽message的變化,message變化時我們就要通知到watch這個鉤子,讓它去執(zhí)行回調函數(shù)。
這個時候message的Dep就收集到了兩個依賴,第二個依賴就是用來管理watch中message變化的。
watch: {
message: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
}
當開發(fā)者自定義computed計算屬性時,如下messageT屬性,是依賴message的變化的。因此message變化時我們也要通知到computed,讓它去執(zhí)行回調函數(shù)。 這個時候message的Dep就收集到了三個依賴,這個依賴就是用來管理computed中message變化的。
computed: {
messageT() {
return this.message + '!';
}
}
圖示如下:一個屬性可能有多個依賴,每個響應式數(shù)據(jù)都有一個Dep來管理它的依賴。

如何收集依賴
我們如何知道data中的某個屬性被使用了,答案就是Object.defineProperty,因為讀取某個屬性就會觸發(fā)get方法??梢詫⒋a進行如下改造:
function defineReactive (obj, key, val) {
let Dep; // 依賴
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
console.log('我被讀了,我要不要做點什么好?');
// 被讀取了,將這個依賴收集起來
Dep.depend(); // 本次新增
return val;
},
set: newVal => {
if (val === newVal) {
return;
}
val = newVal;
// 被改變了,通知依賴去更新
Dep.notify(); // 本次新增
console.log("數(shù)據(jù)被改變了,我要把新的值渲染到頁面上去!");
}
})
}
什么是依賴
那所謂的依賴究竟是什么呢?上面的圖中已經暴露了答案,就是Watcher。
Watcher 「中介」
Watcher就是類似中介的角色,比如message就有三個中介,當message變化,就通知這三個中介,他們就去執(zhí)行各自需要做的變化。
Watcher能夠控制自己屬于哪個,是data中的屬性的還是watch,或者是computed,Watcher自己有統(tǒng)一的更新入口,只要你通知它,就會執(zhí)行對應的更新方法。
因此我們可以推測出,Watcher必須要有的2個方法。一個就是通知變化,另一個就是被收集起來到Dep中去。
class Watcher {
addDep() {
// 我這個Watcher要被塞到Dep里去了~~
},
update() {
// Dep通知我更新呢~~
},
}
總結
回顧一下,Vue響應式原理的核心就是Observer、Dep、Watcher。
Observer中進行響應式的綁定,在數(shù)據(jù)被讀的時候,觸發(fā)get方法,執(zhí)行Dep來收集依賴,也就是收集Watcher。
在數(shù)據(jù)被改的時候,觸發(fā)set方法,通過對應的所有依賴(Watcher),去執(zhí)行更新。比如watch和computed就執(zhí)行開發(fā)者自定義的回調方法。
本篇文章屬于入門篇,能夠先簡單的理解Observer、Dep、Watcher三者的作用和關系。后面會逐漸詳細和深入,循序漸進的理解和學習。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Vue.js@2.6.10更新內置錯誤處機制Fundebug同步支持相應錯誤監(jiān)控
這篇文章主要介紹了Vue.js@2.6.10更新內置錯誤處機制,F(xiàn)undebug同步支持相應錯誤監(jiān)控 ,需要的朋友可以參考下2019-05-05
Element-Ui組件 NavMenu 導航菜單的具體使用
這篇文章主要介紹了Element-Ui組件 NavMenu 導航菜單的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10
如何封裝了一個vue移動端下拉加載下一頁數(shù)據(jù)的組件
這篇文章主要介紹了如何封裝了一個vue移動端下拉加載下一頁數(shù)據(jù)的組件,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01

