当前位置: 首页 > article >正文

vue 双向绑定的原理是什么

Vue 的双向绑定(Two-Way Data Binding)是其核心特性之一,主要通过 数据劫持(Data Observation)发布-订阅模式(Pub-Sub) 实现。其核心原理可分为以下几个关键步骤:


一、Vue 2 的实现原理(基于 Object.defineProperty

1. 数据劫持(Data Observation)
  • Vue 通过 Object.defineProperty 对数据对象的属性进行劫持,将其转换为 getter/setter
  • 当数据被读取时触发 getter,当数据被修改时触发 setter
// 简化版数据劫持实现
function defineReactive(obj, key, val) {
  const dep = new Dep(); // 依赖管理器

  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) { // 依赖收集(当前 Watcher)
        dep.addSub(Dep.target);
      }
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      val = newVal;
      dep.notify(); // 通知所有依赖更新
    }
  });
}
2. 依赖收集与派发更新(Dependency Tracking)
  • 依赖收集:在模板编译时,每个数据属性会关联一个 Dep(依赖管理器),当组件渲染时触发 getter,将当前的 Watcher(订阅者)添加到 Dep 中。
  • 派发更新:当数据变化时,触发 setter,调用 Dep.notify() 通知所有关联的 Watcher 更新视图。
3. 虚拟 DOM 与视图更新
  • 当数据变化触发 Watcher 更新时,Vue 会生成新的虚拟 DOM,通过 Diff 算法对比新旧虚拟 DOM,最终更新真实 DOM。

二、Vue 3 的实现原理(基于 Proxy

Vue 3 使用 Proxy 替代 Object.defineProperty,解决了 Vue 2 的以下问题:

  1. 无法检测对象新增属性(需手动调用 Vue.set)。
  2. 无法监听数组索引和长度的变化(需重写数组方法)。
  3. 性能优化Proxy 直接代理整个对象,无需递归遍历所有属性。
// 简化版 Proxy 实现
function reactive(obj) {
  const handler = {
    get(target, key, receiver) {
      track(target, key); // 依赖收集
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      const oldValue = target[key];
      const result = Reflect.set(target, key, value, receiver);
      if (oldValue !== value) {
        trigger(target, key); // 派发更新
      }
      return result;
    }
  };
  return new Proxy(obj, handler);
}

三、双向绑定的具体实现(v-model 为例)

v-model 是语法糖,本质上是 :value + @input 的封装:

<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">
实现步骤:
  1. 数据劫持:将 message 属性设置为响应式。
  2. 模板编译:将 v-model 解析为 value 属性和 input 事件。
  3. 事件监听:当输入框内容变化时,通过 input 事件更新 message 的值。
  4. 视图更新message 变化触发响应式系统,更新所有依赖该数据的视图。

四、Vue 2 与 Vue 3 的对比

特性Vue 2(Object.defineProperty)Vue 3(Proxy)
对象监听需递归遍历所有属性直接代理整个对象
数组监听需重写数组方法(如 pushpop直接监听索引和长度变化
新增属性支持Vue.set自动支持
性能初始化时递归遍历属性,性能较差惰性劫持,性能更优

五、手写简化版双向绑定

<input id="input">
<div id="display"></div>

<script>
  const data = { text: '' };
  const input = document.getElementById('input');
  const display = document.getElementById('display');

  // 数据劫持
  const reactiveData = new Proxy(data, {
    set(target, key, value) {
      target[key] = value;
      display.textContent = value; // 更新视图
      return true;
    }
  });

  // 视图 → 数据
  input.addEventListener('input', (e) => {
    reactiveData.text = e.target.value;
  });

  // 数据 → 视图(初始化)
  reactiveData.text = 'Hello Vue!';
</script>

总结

  • Vue 2:基于 Object.defineProperty 实现数据劫持,通过 DepWatcher 管理依赖。
  • Vue 3:使用 Proxy 实现更高效、全面的数据劫持,支持动态新增属性和数组索引监听。
  • v-model:本质是 :value + @input 的语法糖,通过响应式系统实现数据与视图的双向同步。

http://www.kler.cn/a/612188.html

相关文章:

  • Linux Namespace(网络命名空间)系列三 --- 使用 Open vSwitch 和 VLAN 标签实现网络隔离
  • Redis 性能数据解读与问题排查优化版
  • 基于Flask的防火墙知识库Web应用技术解析
  • python爬虫PyQt简介
  • 【QT5 多线程示例】信号量
  • C++学习笔记(二十九)——list
  • 【Linux网络-poll与epoll】epollserver设计(两个版本 Reactor)+epoll理论补充(LT ET)
  • vue ts+Windi CSS
  • CTFshow【命令执行】web29-web40 做题笔记
  • 未来工程项目管理新走向:云原生软件赋能绿色可持续建设
  • Kafka 面试备战指南
  • eureka与ribbon混合使用
  • Linux设置SSH免密码密钥登录
  • Netty和Project Reactor如何共同处理大数据流?
  • 无人机抗风测试技术要点概述!
  • failed to load steamui.dll”错误:Steam用户的高频崩溃问题解析
  • LLaMA-Factory使用实战
  • Elasticsearch 之 ElasticsearchRestTemplate 聚合查询
  • Java版Manus实现来了,Spring AI Alibaba发布开源OpenManus实现
  • Linux驱动开发--IIC子系统