發佈/訂閱者模式,以及vue中更新數據原理解析

 發佈/訂閱者模式

要搞清楚vue中的雙向數據綁定原理,就必須理解什麼是發佈、訂閱者模式!!

 1、首先想好誰是發佈者、誰是訂閱者(例如NBA專欄就是發佈者,NBA球迷就是訂閱者)

 2、然後給發佈者添加一個緩存列表,用於存放回調函數用來通知訂閱者

  3、最後就是發佈消息,發佈者者遍歷這個緩存列表,依次觸發訂閱者的回調函數

廢話不多說,直接上代碼!希望大家能讀懂源碼再繼續往下看vue的數據更新原理

        let NBAcol = {}; // 自定義一個NBA專欄對象(這就是發佈者)
        NBAcol.list = []; // 這裏用一個列表來緩存訂閱者的回調函數

        NBAcol.on = function(key, fun) {// on方法是給list數組添加相應的回調函數
            // 如果還沒有訂閱此類消息,給該類消息創建一個緩存列表
            /**
             * list:["xiaoming": [fun], "xiaoqiang": [fun]]
             */
            if(!this.list[key]) {
                this.list[key] = [];
            }
            this.list[key].push(fun); // fun就是通知訂閱者的回調函數
        }

        // 發佈事件
        NBAcol.emit = function(key, value) {
            let funs = this.list[key] // 取出list中對應key的用於通知的回調函數數組
            if(!funs || funs.length === 0){// 如果沒有訂閱過消息,直接返回
                return;
            }
            funs.forEach(fn => {
                fn(value)
            });
        }
        // 取消事件的訂閱
        NBAcol.remove = function(key, fun) {
            let funcs = this.list[key];
            if(!funcs) {
                return false
            }else {
                funs.forEach((item, index) => {
                    if(item === fun) {
                        funcs.splice(index, 1)
                    }
                })
            }
        }

        // 小明的訂閱NBA專欄
        NBAcol.on("xiaoming",function(team) {
            console.log("小明訂閱的球隊是:" + team);
        })
        // 小強的訂閱NBA專欄
        NBAcol.on("xiaoqiang", function(team) {
            console.log("小強訂閱的球隊是:" + team);
        })

        NBAcol.emit("xiaoming",'湖人');
        NBAcol.emit("xiaoqiang", '勇士');

        // 取消訂閱
        NBAcol.remove('xiaoli',function(team){
            console.log("我訂閱的球隊是:"+team)
        });

理解了什麼是發佈,訂閱者模式之後我們再來看看vue中更新數據原理

現象:數據有變化,相關依賴項也會跟着變化

實現:把依賴項的方法(回調函數)封裝在Dep依賴項裏面,數據變化時下發通知Dep重新執行相關方法

let data = {price: 5, quantity: 2};
let target = null;

class Dep{
    constructor() {
        this.subscribers = [];
    }
    depend() {
        if(target && !this.subscribers.includes(target)) { // target就是對數據的一些操作,即添加訂閱者
            this.subscribers.push(target)
        }
    }
    notify() {
        this.subscribers.forEach(sub => sub())
    }
}

//使用Object.defineProperty來更新data裏面的數據
Object.keys(data).forEach(key => {
    let internalValue = data[key];

    const dep = new Dep;
    Object.defineProperty(data, key, {
        get(){
            dep.depend();
            return internalValue;
        },
        set(newValue) {
            internalValue = newValue;
            dep.notify();
        }
    })
})

function watcher(myFunc) {
    target = myFunc;
    target();
    target = null;
}

watcher(() => {
    data.total = data.price * data.quantity;
})

data.price = 200;

console.log(data.total);

總結:

1、首先建立一個Dep依賴項(類):在Dep類裏面定義一個subscrers數組用於收集訂閱者的回調函數(depend())並且逐個執行(notify());

  2、創建數據的劫持: 使用Object.keys(data);遍歷data的key,然後通過Object.defineProperty(data, key, {...})來更新data裏的數據;  並在遍歷中實例化Dep且在get中執行dep.depend(),在get()中執行dep.notify(),注意這裏是更新後再執行!

 3、最後就是定義一個監聽函數watcher()了;該函數參數是一個函數,把該函數賦值給target並執行之後置爲null,taget就是一個訂閱者的回調函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章