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

 更新时间:2025年02月06日 09:47:43   作者:清风 与我  
Vue2.x通过Object.defineProperty()实现响应式系统,但存在一些限制,如不能检测新增和删除的属性、深层嵌套对象性能开销大等,Vue3.0引入Proxy,可以更高效地拦截对象操作,解决这些问题

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。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • vue @ ~ 相对路径 路径别名设置方式

    vue @ ~ 相对路径 路径别名设置方式

    这篇文章主要介绍了vue @ ~ 相对路径 路径别名设置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • 详解vue3沙箱机制

    详解vue3沙箱机制

    这篇文章主要介绍了详解vue3 沙箱机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • vue中使用echarts制作圆环图的实例代码

    vue中使用echarts制作圆环图的实例代码

    这篇文章主要介绍了vue中使用echarts制作圆环图的实例代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-07-07
  • vue2中vue-router引入使用详解

    vue2中vue-router引入使用详解

    Vue Router 是 Vue 的官方路由,它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举,下面就跟随小编一起学习一下vue-router的具体用法吧
    2023-12-12
  • Vuex的使用及知识点笔记

    Vuex的使用及知识点笔记

    这篇文章主要介绍了Vuex的使用及知识点笔记,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • vue时间戳和时间的相互转换方式

    vue时间戳和时间的相互转换方式

    本文通过示例代码介绍了vue时间戳和时间的相互转换方式,通过场景分析介绍了vue3使用组合式api将时间戳格式转换成时间格式(2023年09月28日 10:00),感兴趣的朋友一起看看吧
    2023-12-12
  • vue2如何使用vue-i18n搭建多语言切换环境

    vue2如何使用vue-i18n搭建多语言切换环境

    这篇文章主要介绍了vue2-使用vue-i18n搭建多语言切换环境的相关知识,在data(){}中获取的变量存在更新this.$i18n.locale的值时无法自动切换的问题,需要刷新页面才能切换语言,感兴趣的朋友一起看看吧
    2023-12-12
  • Vue 样式绑定的实现方法

    Vue 样式绑定的实现方法

    学习 Vue 的时候觉得样式绑定很简单,但是使用的时候总是容易搞晕自己。这篇文章主要介绍了Vue 样式绑定的实现方法,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • vue3.0透传属性和事件的使用方式举例

    vue3.0透传属性和事件的使用方式举例

    这篇文章主要给大家介绍了关于vue3.0透传属性和事件使用的相关资料,透传attribute指的是传递给一个组件,却没有被该组件声明为props或emits的attribute或者v-on事件监听器,需要的朋友可以参考下
    2024-01-01
  • 基于vue-router的matched实现面包屑功能

    基于vue-router的matched实现面包屑功能

    本文主要介绍了基于vue-router的matched实现面包屑功能,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09

最新评论