深究Vue2和Vue3的雙向數(shù)據(jù)綁定原理(非AI內(nèi)容)

八股文說,vue2使用Object.definedPropoty,而Vue3使用的是Proxy。這很難理解,于是我深入調(diào)查一番原理層面,如下純手寫,非AI。

如果我是Vue開發(fā)者,想要實(shí)現(xiàn)“雙向數(shù)據(jù)綁定”的需求,也就是對(duì)象obj中的數(shù)據(jù),被讀取或者被更新之后,我都要想辦法收到通知,并且能夠做一些其他的操作,比如觸發(fā)頁面更新等操作。

const const obj = {
  a: 1,
  b: 2,
  c: {
    d: 3,
    e: 4,
  },
};
obj.a // 我并不知道“讀取”操作了
obj.a = 1 // 我并不知道“更新”操作了

上面的操作,我沒法收到通知,做相關(guān)的處理,那怎么辦呢?

我要是能重新修改obj對(duì)象的set、get方法就好了,這樣“讀取”會(huì)默認(rèn)走get方法,“更新”會(huì)默認(rèn)走set方法。

Object.definedPropoty就可以做到,實(shí)現(xiàn)代碼如下:

// vue2
function observer(dataSource: { [key: string]: any }) {
  for (let key in dataSource) {  // [注釋1]
    const value = dataSource[key];
    if(typeof value === 'object' && value !== null) {
      observer(value);  // [注釋2]
    }
    const listener = Object.defineProperty(dataSource, key, {
      set: (sth) => {
        if(key === sth) return;
        console.log('修改更新', key);
        key = sth;
      },
      get: () => {
        console.log('讀取', key);
        return value;
      },
    });
    listener;
  }
  return dataSource;
}

const listener = observer(obj);
listener.c.d;
listener.c.d = 1111;
listener.c.d;
console.log('lister<<<<<<<<', listener);
  • 注釋1:Object.defineProperty只能寫對(duì)應(yīng)的key,因此每次需要遍歷整個(gè)對(duì)象所有的key,依次包裹。這也是Vue2的弊端——需要提前整體遍歷對(duì)象,效率比較差。而且只能操作已有的數(shù)據(jù)。

  • 注釋2:如果對(duì)象包含了對(duì)象,需要做遞歸處理,而這一步,需要操作之前就要執(zhí)行,效率差。

以上就是Vue2實(shí)現(xiàn)雙向數(shù)據(jù)綁定的原理,總結(jié)來說,Vue2有以下弊端:

  1. 需要提前整體遍歷對(duì)象每一個(gè)數(shù)據(jù),效率低;
  2. 數(shù)組的方法無法觸發(fā)set\get,因此Vue2對(duì)數(shù)組的方法做了重構(gòu);
  3. 只能對(duì)已有的數(shù)據(jù)做操作,如果新增數(shù)據(jù),比如obj['aaa'],則無法獲得監(jiān)聽,因?yàn)闆]有被Object.definedPropoty包裹。

那么Vue3做了什么呢?

// vue3
function isObject(obj: any) {
  return typeof obj === 'object' && obj !== null;
}
function observer(dataSource: { [key: string]: any }) {
  const proxy = new Proxy(dataSource, {
    get(target, prop) {
      if (isObject(target[prop as keyof typeof target])) {
        return observer(target[prop as keyof typeof target]);
      }
      console.log('讀取操作');
      return target;
    },
    set(target, prop, val) {
      if (target.prop === val) return;
      if (isObject(target[prop as keyof typeof target])) {
        return observer(target[prop as keyof typeof target]);
      }
      target[prop as keyof typeof target] = val;
      console.log('更新操作');
      return true;
    },
  });
  return proxy;
}

Proxy可以自動(dòng)監(jiān)聽整個(gè)對(duì)象,而且支持?jǐn)?shù)組,無需提前遍歷。

the end!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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