深究Vue2和Vue3的双向数据绑定原理(非AI内容)

八股文说,vue2使用Object.definedPropoty,而Vue3使用的是Proxy。这很难理解,于是我深入调查一番原理层面,如下纯手写,非AI。

如果我是Vue开发者,想要实现“双向数据绑定”的需求,也就是对象obj中的数据,被读取或者被更新之后,我都要想办法收到通知,并且能够做一些其他的操作,比如触发页面更新等操作。

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

上面的操作,我没法收到通知,做相关的处理,那怎么办呢?

我要是能重新修改obj对象的set、get方法就好了,这样“读取”会默认走get方法,“更新”会默认走set方法。

Object.definedPropoty就可以做到,实现代码如下:

// 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只能写对应的key,因此每次需要遍历整个对象所有的key,依次包裹。这也是Vue2的弊端——需要提前整体遍历对象,效率比较差。而且只能操作已有的数据。

  • 注释2:如果对象包含了对象,需要做递归处理,而这一步,需要操作之前就要执行,效率差。

以上就是Vue2实现双向数据绑定的原理,总结来说,Vue2有以下弊端:

  1. 需要提前整体遍历对象每一个数据,效率低;
  2. 数组的方法无法触发set\get,因此Vue2对数组的方法做了重构;
  3. 只能对已有的数据做操作,如果新增数据,比如obj['aaa'],则无法获得监听,因为没有被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可以自动监听整个对象,而且支持数组,无需提前遍历。

the end!

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容