Vue響應式原理-理解Observer、Dep、Watcher

開篇

最近在學習Vue的源碼,看了網上一些大神的博客,看起來感受仍是蠻吃力的。本身記錄一下學習的理解,但願可以達到簡單易懂,不看源碼也能理解的效果😆javascript

若是有錯誤,懇求大佬們指點嘿😋html

Object.defineProperty

相信不少同窗或多或少都瞭解Vue的響應式原理是經過Object.defineProperty實現的。被Object.defineProperty綁定過的對象,會變成「響應式」化。也就是改變這個對象的時候會觸發get和set事件。進而觸發一些視圖更新。舉個栗子🌰vue

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("數據被改變了,我要把新的值渲染到頁面上去!");
        }
    })
}

let data = {
    text: 'hello world',
};

// 對data上的text屬性進行綁定
defineReactive(data, 'text', data.text);

console.log(data.text); // 控制檯輸出 <我被讀了,我要不要作點什麼好?>
data.text = 'hello Vue'; // 控制檯輸出 <hello Vue && 數據被改變了,我要把新的值渲染到頁面上去!>
複製代碼

Observer 「響應式」

Vue中用Observer類來管理上述響應式化Object.defineProperty的過程。咱們能夠用以下代碼來描述,將this.data也就是咱們在Vue代碼中定義的data屬性所有進行「響應式」綁定。java

class Observer {
    constructor() {
        // 響應式綁定數據經過方法
    	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中的數據進行響應式後,雖然能夠監聽到數據的變化了,那咱們怎麼處理通知視圖就更新呢?git

Dep就是幫咱們收集【究竟要通知到哪裏的】。好比下面的代碼案例,咱們發現,雖然data中有textmessage屬性,可是隻有message被渲染到頁面上,至於text不管怎麼變化都影響不到視圖的展現,所以咱們僅僅對message進行收集便可,能夠避免一些無用的工做。github

那這個時候messageDep就收集到了一個依賴,這個依賴就是用來管理datamessage變化的。異步

<div>
    <p>{{message}}</p>
</div>
複製代碼
data: {
    text: 'hello world',
    message: 'hello vue',
}
複製代碼

當使用watch屬性時,也就是開發者自定義的監聽某個data中屬性的變化。好比監聽message的變化,message變化時咱們就要通知到watch這個鉤子,讓它去執行回調函數。函數

這個時候messageDep就收集到了兩個依賴,第二個依賴就是用來管理watchmessage變化的。post

watch: {
    message: function (val, oldVal) {
        console.log('new: %s, old: %s', val, oldVal)
    },
}        
複製代碼

當開發者自定義computed計算屬性時,以下messageT屬性,是依賴message的變化的。所以message變化時咱們也要通知到computed,讓它去執行回調函數。 這個時候messageDep就收集到了三個依賴,這個依賴就是用來管理computedmessage變化的。學習

computed: {
    messageT() {
        return this.message + '!';
    }
}
複製代碼

圖示以下:一個屬性可能有多個依賴,每一個響應式數據都有一個Dep來管理它的依賴。

依賴收集

如何收集依賴

咱們如何知道data中的某個屬性被使用了,答案就是Object.defineProperty,由於讀取某個屬性就會觸發get方法。能夠將代碼進行以下改造:

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("數據被改變了,我要把新的值渲染到頁面上去!");
        }
    })
}
複製代碼

什麼是依賴

那所謂的依賴到底是什麼呢?上面的圖中已經暴露了答案,就是Watcher

Watcher 「中介」

Watcher就是相似中介的角色,好比message就有三個中介,當message變化,就通知這三個中介,他們就去執行各自須要作的變化。

Watcher可以控制本身屬於哪一個,是data中的屬性的仍是watch,或者是computedWatcher本身有統一的更新入口,只要你通知它,就會執行對應的更新方法。

所以咱們能夠推測出,Watcher必需要有的2個方法。一個就是通知變化,另外一個就是被收集起來到Dep中去。

class Watcher {
    addDep() {
        // 我這個Watcher要被塞到Dep裏去了~~
    },
    update() {
        // Dep通知我更新呢~~
    }, 
}
複製代碼

總結

回顧一下,Vue響應式原理的核心就是ObserverDepWatcher

Observer中進行響應式的綁定,在數據被讀的時候,觸發get方法,執行Dep來收集依賴,也就是收集Watcher

在數據被改的時候,觸發set方法,經過對應的全部依賴(Watcher),去執行更新。好比watchcomputed就執行開發者自定義的回調方法。

本篇文章屬於入門篇,可以先簡單的理解ObserverDepWatcher三者的做用和關係。後面會逐漸詳細和深刻,按部就班的理解和學習。

若是你以爲對你有幫助,就點個贊吧~

正在書寫的系列~

Github博客 歡迎交流~

相關文章
相關標籤/搜索