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

相关推荐
lichenyang4537 分钟前
从 Express 老项目到 NestJS + Docker:一次车辆管理系统的渐进式重构
前端
Momo__1 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
程序员小富1 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇1 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇1 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆2 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马2 小时前
Verilog开发常见问题汇总解析
前端
子兮曰2 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端
weedsfly2 小时前
语法糖褪去之后——Babel 转译产物中的 JavaScript 本貌
前端·javascript