当前位置:首页 > VUE

vue双向绑定代码实现

2026-02-24 15:52:07VUE

Vue 双向绑定的实现原理

Vue 的双向绑定主要通过数据劫持结合发布者-订阅者模式实现。核心是利用 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)来监听数据变化,并通过事件机制通知视图更新。

Vue 2.x 实现代码示例

数据劫持与监听

function defineReactive(obj, key, val) {
  const dep = new Dep(); // 依赖收集器

  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) {
        dep.addSub(Dep.target); // 收集当前依赖
      }
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      val = newVal;
      dep.notify(); // 通知所有订阅者更新
    }
  });
}

依赖收集器(Dep)

class Dep {
  constructor() {
    this.subs = []; // 订阅者列表
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}
Dep.target = null; // 静态属性,指向当前正在处理的 Watcher

订阅者(Watcher)

vue双向绑定代码实现

class Watcher {
  constructor(vm, exp, cb) {
    this.vm = vm;
    this.exp = exp;
    this.cb = cb;
    this.value = this.get(); // 触发依赖收集
  }
  get() {
    Dep.target = this;
    const value = this.vm.data[this.exp]; // 触发 getter
    Dep.target = null;
    return value;
  }
  update() {
    const newValue = this.vm.data[this.exp];
    if (newValue !== this.value) {
      this.value = newValue;
      this.cb.call(this.vm, newValue);
    }
  }
}

编译器(Compiler)

function compile(node, vm) {
  if (node.nodeType === 1) { // 元素节点
    const attrs = node.attributes;
    for (let i = 0; i < attrs.length; i++) {
      if (attrs[i].nodeName === 'v-model') {
        const exp = attrs[i].nodeValue;
        node.addEventListener('input', e => {
          vm.data[exp] = e.target.value;
        });
        new Watcher(vm, exp, value => {
          node.value = value;
        });
      }
    }
  }
}

Vue 3.x 实现代码示例(基于 Proxy)

响应式数据

vue双向绑定代码实现

function reactive(obj) {
  const handlers = {
    get(target, key, receiver) {
      track(target, key); // 依赖收集
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver);
      trigger(target, key); // 触发更新
      return true;
    }
  };
  return new Proxy(obj, handlers);
}

const targetMap = new WeakMap(); // 存储依赖关系
function track(target, key) {
  if (!activeEffect) return;
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }
  dep.add(activeEffect);
}

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const effects = depsMap.get(key);
  effects && effects.forEach(effect => effect());
}

副作用函数(Effect)

let activeEffect = null;
function effect(fn) {
  activeEffect = fn;
  fn();
  activeEffect = null;
}

双向绑定示例

HTML 模板

<input id="input" type="text" v-model="message">
<div id="output"></div>

初始化 Vue 实例

const vm = {
  data: reactive({ message: '' }),
  mount() {
    effect(() => {
      document.getElementById('output').textContent = vm.data.message;
    });
    document.getElementById('input').addEventListener('input', e => {
      vm.data.message = e.target.value;
    });
  }
};
vm.mount();

关键点总结

  • Vue 2.x 使用 Object.defineProperty 监听对象属性,需递归遍历对象并劫持每个属性。
  • Vue 3.x 使用 Proxy 直接代理整个对象,无需递归且能监听新增/删除属性。
  • 依赖收集通过 DepWatcher(Vue 2.x)或 effect(Vue 3.x)实现。
  • 视图更新通过事件监听(如 input)和响应式数据变更触发。

标签: 绑定双向
分享给朋友:

相关文章

vue实现绑定输入参数

vue实现绑定输入参数

Vue 实现绑定输入参数 在 Vue 中,可以通过 v-model 指令实现输入框与数据的双向绑定。以下是一个简单的示例: <template> <div> &l…

vue 实现循环绑定

vue 实现循环绑定

Vue 实现循环绑定的方法 在 Vue 中,可以通过 v-for 指令实现循环绑定,用于渲染列表或对象数据。以下是几种常见的实现方式: 数组循环绑定 使用 v-for 遍历数组,可以通过索引或直接获…

php实现双向队列

php实现双向队列

PHP 实现双向队列的方法 双向队列(Deque,Double-ended Queue)是一种允许在队列两端进行插入和删除操作的数据结构。PHP 中可以通过数组或 SplDoublyLinkedLis…

vue 实现双向绑定

vue 实现双向绑定

Vue 双向绑定的实现原理 Vue 的双向绑定主要通过 v-model 指令实现,底层基于数据劫持和发布-订阅模式。以下是核心实现机制: 数据劫持(响应式系统) Vue 使用 Object.def…

vue实现绑定页面

vue实现绑定页面

Vue 实现数据绑定到页面的方法 Vue.js 通过数据绑定机制实现数据和页面的同步更新,主要依赖响应式系统和模板语法。以下是几种常见的绑定方式: 双向绑定 (v-model) 使用 v-mode…

实现vue数据绑定

实现vue数据绑定

Vue数据绑定的实现方式 Vue.js通过数据劫持和发布-订阅模式实现数据绑定,核心是响应式系统。以下是主要实现方法: 双向数据绑定(v-model) <input v-model="mes…