Vue2与Vue3的双向数据绑定原理与实现对比

双向数据绑定原理

Vue实现数据双向绑定核心原理是MVVM模式
  • Model(数据层):Vue中的数据模型,用来存储数据
  • View(视图层):Vue中的视图界面,由HTML模板和Vue指令组成
  • ViewModel(业务逻辑层):核心,是一个Vue实例,充当Model和View的桥梁
    • 数据变化时,通过监听器(Observer)触发依赖通知,更新视图层。
    • 用户操作视图时,解析器(Compiler)捕获事件(如v-model输入),更新数据层。

双向绑定的实现

监听器
  • 用于监听数据变化,方式有数据劫持、发布-订阅模式

  • 数据劫持

    • Vue2 :使用Object.defineProperty来拦截对象属性的gettersetter,在getter中收集依赖,在数据变化时通知视图更新 ,在setter中,通过触发依赖收集中的更新函数,实现视图更新 。(Object.defineProperty() - JavaScript | MDN

      js 复制代码
      //示例
      const o = {};
      let bValue = 38;
      Object.defineProperty(o, "b", {
        get() {
          //收集依赖
          return bValue;
        },
        set(newValue) {
          bValue = newValue;
          // 触发视图更新
        },
      });
    • Vue3 :使用Proxy代替Object.defineProperty,用于创建一个对象的代理,从而实现基本操作的拦截和自定义,调用get方法时,会进行依赖收集 ,调用set方法时,会进行依赖更新Proxy - JavaScript | MDN

      • 语法:const p = new Proxy(target, handler)

      • 参数:

        • target

          要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

        • handler

          一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

      js 复制代码
      //示例
      let products = new Proxy(
        {
          browsers: ["Internet Explorer", "Netscape"],
        },
        {
          get: function (obj, prop) {
            // 收集依赖
            return obj[prop];
          },
          set: function (obj, prop, value) {
            obj[prop] = value;
            // 触发视图更新,表示成功
            return true;
          },
        },
      );
  • 发布-订阅模式

    • Vue.js 通过这个模式来管理组件和数据之间的依赖关系。每当数据变化时,依赖于这些数据的视图(或者是观察者)会被通知并更新

      js 复制代码
      // 1. 发布者类
      class Dep {
        constructor() {
          this.subscribers = [];
        }
        // 依赖收集
        depend() {
          if (Dep.target && !this.subscribers.includes(Dep.target)) {
            this.subscribers.push(Dep.target);
          }
        }
        // 通知更新
        notify() {
          this.subscribers.forEach(sub => sub());
        }
      }
      
      // 2. 
      // ①数据劫持 Vue2-Start
      function defineReactive(obj, key, val) {
        const dep = new Dep();
        Object.defineProperty(obj, key, {
          get() {
            dep.depend();      // 收集依赖
            return val;
          },
          set(newVal) {
            val = newVal;
            dep.notify();      // 触发更新
          }
        });
      }
      // Vue2-End
      
      // ②响应式代理 Vue3-Start
      function reactive(obj) {
        const deps = new Map(); // 存储每个属性的依赖
      
        return new Proxy(obj, {
          get(target, key) {
            let dep = deps.get(key);
            if (!dep) {
              dep = new Dep();
              deps.set(key, dep);
            }
            if (Dep.currentEffect) {
              dep.depend(Dep.currentEffect); // 收集依赖
            }
            return target[key];
          },
          set(target, key, value) {
            target[key] = value;
            deps.get(key)?.notify(); // 触发更新
            return true;
          }
        });
      }
      // Vue3-End
      
      // 3. 观察者函数
      function watch(effect) {
        Dep.target = effect;   // 标记当前依赖
        effect();              // 首次执行以触发getter
        Dep.target = null;     // 重置
      }
      
      // 使用示例
      const data = {};
      defineReactive(data, 'count', 0);
      
      // 订阅数据变化
      watch(() => {
        console.log('Count updated:', data.count);
      });
      
      // 触发更新
      data.count = 1; // 输出: "Count updated: 1"
  1. Compile 模板解析

    • 实现视图到数据的绑定

    • 解析阶段

      • 遍历DOM树,识别指令(如v-model{{}}插值)。
      • 为每个指令创建对应的更新函数,绑定到数据依赖。
    • 绑定示例

      js 复制代码
      function compileInput(node, data, key) {
        node.value = data[key]; // 初始化值
        node.addEventListener('input', (e) => {
          data[key] = e.target.value; // View → Model
        });
        // 订阅数据变化,更新视图
        watch(key, (value) => node.value = value); // Model → View
      }
    • 虚拟DOM优化

      • 将模板转换为轻量级的虚拟DOM树。
      • 数据变化时,生成新虚拟DOM,通过Diff算法比对差异,仅更新真实DOM的必要部分。

参考资料 juejin.cn/post/684490... zhuanlan.zhihu.com/p/685914161

相关推荐
HtwHUAT21 分钟前
五、web自动化测试01
前端·css·chrome·python·功能测试·selenium·html
86Eric24 分钟前
Vue 中 使用 Mixins 解决 多页面共用相同组件的相关问题
前端·javascript·vue.js·mixins·公用组件
qq_252496399636 分钟前
react 子组件暴露,父组件接收
前端·javascript·react.js
fakaifa39 分钟前
【最新版】西陆健身系统源码全开源+uniapp前端
前端·小程序·uni-app·开源·php·约课小程序·健身小程序
南囝coding1 小时前
关于我的第一个产品!
前端·后端·产品
iOS阿玮1 小时前
别等了,今天是Xcode15时代的最后一天。
前端·app·apple
沙尘暴炒饭1 小时前
vuex持久化vuex-persistedstate,存储的数据刷新页面后导致数据丢失
开发语言·前端·javascript
2401_837088501 小时前
CSS清楚默认样式
前端·javascript·css
zwjapple1 小时前
React 的 useEffect 清理函数详解
前端·react.js·前端框架
Jewel1051 小时前
如何配置Telegram Mini-App?
前端·vue.js·app