当前位置: 代码网 > it编程>编程语言>Javascript > Vue2和Vue3的双向数据绑定原理分析

Vue2和Vue3的双向数据绑定原理分析

2025年02月13日 Javascript 我要评论
vue2.x 是如何实现响应式系统的当你把一个普通的 js 对象传入 vue 实例作为 data 选项,vue 将遍历此对象的所有prototype(属性),并使用 object.defineprop

vue2.x 是如何实现响应式系统的

当你把一个普通的 js 对象传入 vue 实例作为 data 选项,vue 将遍历此对象的所有prototype(属性),并使用 object.defineproperty(),将这些 prototype(属性),全部转换为 getter / setter,在 getter 中收集数据依赖,在 setter 中监听数据变化,一旦数据发生改变,在通知订阅者。

每个组件实例,都对应一个 watcher 实例,它会在组件渲染的过程把 “接触” 过的数据 prototype(属性)记录为依赖,之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染;

defineproperty 的痛点

  1. 它无法发现对象新增和被删除的属性,当你给一个对象添加一个新的属性,这个新增的属性没有添加到 vue 的数据更新侦查机制里;

vue.set() 可以让 vue 知道你新增了一个属性,其实 vue.set 可以让 vue 知道新增了一个属性。其实 set() 内部也是通过调用 defineproperty() 来实现;

  1. 当你利用索引直接设置一个数组(new array(4))或者修改数组的长度时,vue 不能检测到数组的变动;
  2. 当对象嵌套的层数特别深(多层嵌套)的时候,递归遍历带来的性能开销就会比较大;

object.defineproperty 代码的使用

mounted() {
    // 先定义好一套规则
    class observer {
      constructor(data) {
        for (let key of object.keys(data)) {
          if (typeof data[key] === "object") {
            data[key] = new observer(data[key]);
          }
          object.defineproperty(this, key, {
            enumerable: true,
            configurable: true,
            get() {
              console.log("you visited" + key);
              return data[key];
            },
            set(newvalue) {
              console.log("you set" + key);
              console.log("new value" + newvalue);
              if (newvalue === data[key]) {
                return;
              }
              data[key] = newvalue;
            },
          });
        }
      }
    }
    let obj = {
      name: "app",
      age: 18,
      a: {
        b: 1,
        c: {
          d: 1,
        },
      },
    };
    let app = new observer(obj);
    console.log(app);
    app.age = 20;
    app.newproperty = "new attrs";
    console.log(app);
  },

结果:

proxy 方法的理解

proxy 在 vue3.0 中上位

可以解决 defineproperty 的痛点,因为本质的原因在于 proxy 是内置了拦截器对象。所有的外部访问都得先经过这一层拦截,不管是先前定义好的,还是新增的属性,又或者是深层的嵌套属性,访问时都会被拦截;

reflect.set()方法用于设置对象属性的值,1:目标对象:2:改变参数的名称:3:改变参数的值:4:值是this如果遇到设置器,将提供给目标调用。

此方法返回一个布尔值,该值指示该属性是否已成功设置。

proxy 代码的使用

mounted() {
    const obj = {
      name: "app",
      age: 19,
      a: {
        b: 1,
        c: 2,
      },
    };
    const p = new proxy(obj, {
      get(target, propkey, receiver) {
        console.log("your visited:" + propkey);
        // reflect.set()方法用于设置对象属性的值:1:目标对象:2:改变参数的名称:3:改变参数的值
        // 此方法返回一个布尔值,该值指示该属性是否已成功设置。
        return reflect.set(target, propkey, receiver);
      },
      set(target, propkey, value, receiver) {
        console.log("you set:" + propkey);
        console.log("new value:" + value);
        // reflect.set()方法用于设置对象属性的值,1:目标对象:2:改变参数的名称:3:改变参数的值:4:值是this如果遇到设置器,将提供给目标调用。
        // 此方法返回一个布尔值,该值指示该属性是否已成功设置。
        return reflect.set(target, propkey, value, receiver);
      },
    });
    p.age = "20";
    console.log(p);
    p.newproperty = "new attribute";
    console.log(p);
  },

结果:

总结

  1. proxy 是用来操作对象并且扩展对象能到,而 object.defineproperty() 只是单纯的操作对象的属性;
  2. vue2.x 是用 object.defineproperty() 实现数据响应的,但是受限于 defineproperty() 的实现,必须递归遍历至对象的最底层;
  3. vue3.0 用 proxy 来拦截对象,不管对目标执行任何操作,都会先通过 proxy 的处理逻辑;
  4. 除了 vue3.0,还有其他的库也在使用 proxy。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com