Vue響應(yīng)式基本原理

Vue響應(yīng)式原理

Vue響應(yīng)式系統(tǒng)基本原理

Object.defineProperty

Object.defineProperty(obj, prop, descriptor)

這是實(shí)現(xiàn)響應(yīng)式的基礎(chǔ),通過對象屬性描述對象來控制objprop

{
  value: 'static' 
  writable: true,
  enumerable: true
  configurable: true
  get: function(){}
  set: function(){}
}

上面六個(gè)對象屬性的描述屬性,可以看作是控制屬性的屬性

實(shí)現(xiàn)Observer

首先定義一個(gè)更新函數(shù)來模擬視圖更新

function updata (val) {
    console.log("視圖更新了哈~");
}

然后定義一個(gè)defineReact函數(shù)通過Object.defineProperty(obj, prop, descriptor),來進(jìn)行數(shù)據(jù)劫持,實(shí)現(xiàn)對象的響應(yīng)化。

當(dāng)一個(gè)對象的屬性一旦定義了取值函數(shù)get(或存值函數(shù)set),就不能將writable屬性設(shè)為true,或者同時(shí)定義value屬性,否則會(huì)報(bào)錯(cuò)。

經(jīng)過defineReact函數(shù)處理后,對象讀取屬性時(shí)會(huì)觸發(fā)get,修改屬性值時(shí)會(huì)觸發(fā)set

function defineReact (obj, key, value) {
    Object.defineProperty(obj, key, {
        enumerable: true,       
        configurable: true,     
        get: function reactGetter () {
            return value;         
        },
        set: function reactSetter (newValue) {
            if (newValue === value) return;
            updata(newValue);
        }
 });

通過遞歸遍歷將一個(gè)obj的屬性都轉(zhuǎn)化為由存取器控制,為方便理解去掉遞歸的過程

function observer (targetObj) {
    if (!targetObj || (typeof targetObj !== 'object')) {
        return;
    }
    Object.keys(targetObj).forEach((key) => {
        defineReact(targetObj, key, targetObj[key]);
    });
}

最后來看看在Vue中的實(shí)現(xiàn)

class Vue {
    //Vue的構(gòu)造函數(shù)
    constructor(options) {
        this._data = options.data;
        observer(this._data);
    }
}

我們在使用Vue生成實(shí)例的時(shí)候,就會(huì)將data中的數(shù)據(jù)實(shí)現(xiàn)Observe,響應(yīng)化

const obj = new Vue({
  data: {
      text: 'hello'
  }
})

總結(jié),一旦觸發(fā)setter函數(shù),Vue將會(huì)自動(dòng)判斷是否要觸發(fā)視圖更新函數(shù),來更新頁面,實(shí)現(xiàn)響應(yīng)式。

響應(yīng)式系統(tǒng)的依賴收集

依賴收集會(huì)讓 data中的某個(gè)數(shù)據(jù)知道有多少個(gè)地方依賴我的數(shù)據(jù),在我變化的時(shí)候需要通知它們。

創(chuàng)建一個(gè)發(fā)布訂閱模式GatherWatcher來接收依賴數(shù)據(jù)的更新,它的主要作用就是將在Vue實(shí)例中依賴data中某個(gè)數(shù)據(jù)的所有地方(watcher)收集起來,統(tǒng)一管理。

class GatherWatcher {
    constructor () {
        /* 用來存放Watcher對象的數(shù)組 */
        this.subs = [];
    }

    /* 在subs中添加一個(gè)Watcher對象 */
    addSub (sub) {
        this.subs.push(sub);
    }

    /* 通知所有Watcher對象更新視圖 */
    notify () {
        this.subs.forEach((sub) => {
            sub.update();
        })
    }
}

接下來我們修改一下 defineReact 以及 Vue 的構(gòu)造函數(shù),來完成依賴收集。

function defineReactive (obj, key, val) {
   //一個(gè)watchers對象,來收集對某個(gè)數(shù)據(jù)的的watcher
    const watchers = new GatherWatcher();
    
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactGetter () {
            //在get的時(shí)候?qū)?dāng)前的Watcher對象存入watchera的subs中 
            watchers.addSub(watcher);
            return val;         
        },
        set: function reactiveSetter (newVal) {
            if (newVal === val) return;
            // 在set的時(shí)候觸發(fā)watchers的notify來通知所有的watcher對象更新視圖 
            watchers.notify();
        }
    });
}

class Vue {
    constructor(options) {
        this._data = options.data;
        observer(this._data);
        /* 新建一個(gè)Watcher觀察者對象,這時(shí)候wacther會(huì)指向這個(gè)Watcher對象 */
        new Watcher();
        console.log('render', this._data.test);//模擬render
    }
}

總結(jié)一下

1. 通過`Object.defineProperty` 把這些屬性全部轉(zhuǎn)為 `getter/setter`,進(jìn)行數(shù)據(jù)劫持
2. 在某個(gè)數(shù)據(jù)`getter`時(shí),通過訂閱,收集對這個(gè)數(shù)據(jù)的依賴者們(watcher)
3. 在數(shù)據(jù)setter時(shí),發(fā)布更新`Notify`,通知這個(gè)數(shù)據(jù)的訂閱者們進(jìn)行更新,重新渲染。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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