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

相关推荐
LaoZhangAI2 分钟前
GPT-5推理能力全解析:o3架构、链式思考与2025年8月发布
前端·后端
JuneXcy18 分钟前
11.Layout-Pinia优化重复请求
前端·javascript·css
子洋28 分钟前
快速目录跳转工具 zoxide 使用指南
前端·后端·shell
天下无贼!29 分钟前
【自制组件库】从零到一实现属于自己的 Vue3 组件库!!!
前端·javascript·vue.js·ui·架构·scss
CF14年老兵1 小时前
✅ Next.js 渲染速查表
前端·react.js·next.js
司宸1 小时前
学习笔记八 —— 虚拟DOM diff算法 fiber原理
前端
阳树阳树1 小时前
JSON.parse 与 JSON.stringify 可能引发的问题
前端
让辣条自由翱翔1 小时前
总结一下Vue的组件通信
前端
dyb1 小时前
开箱即用的Next.js SSR企业级开发模板
前端·react.js·next.js
前端的日常1 小时前
Vite 如何处理静态资源?
前端