当前位置:首页 > VUE

vue自己实现双向绑定

2026-01-21 10:52:25VUE

实现 Vue 双向绑定的核心原理

双向绑定的核心是通过数据劫持结合发布-订阅模式实现的。主要涉及 Object.definePropertyProxy 来监听数据变化,并通过观察者模式通知视图更新。

使用 Object.defineProperty 实现

通过 Object.defineProperty 劫持对象的属性,在属性被访问或修改时触发 getter 和 setter。

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 类)

Dep 类用于管理依赖(Watcher 实例),并在数据变化时通知所有订阅者。

vue自己实现双向绑定

class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}
Dep.target = null;

实现观察者(Watcher 类)

Watcher 类用于订阅数据变化,并在数据更新时执行回调(如更新视图)。

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];
    Dep.target = null;
    return value;
  }
  update() {
    const newValue = this.vm._data[this.exp];
    if (newValue !== this.value) {
      this.value = newValue;
      this.cb(newValue);
    }
  }
}

实现数据监听

遍历对象的所有属性,递归监听嵌套对象。

vue自己实现双向绑定

function observe(obj) {
  if (!obj || typeof obj !== 'object') return;
  Object.keys(obj).forEach(key => {
    defineReactive(obj, key, obj[key]);
    observe(obj[key]);
  });
}

实现 Vue 类

将数据、观察者和模板绑定整合。

class Vue {
  constructor(options) {
    this._data = options.data;
    observe(this._data);
    this._compile(options.el);
  }
  _compile(el) {
    const element = document.querySelector(el);
    const nodes = element.children;
    Array.from(nodes).forEach(node => {
      if (node.hasAttribute('v-model')) {
        const exp = node.getAttribute('v-model');
        node.addEventListener('input', e => {
          this._data[exp] = e.target.value;
        });
        new Watcher(this, exp, val => {
          node.value = val;
        });
      }
    });
  }
}

使用 Proxy 实现(现代方式)

Proxy 可以更简洁地实现数据劫持,支持监听数组变化。

function reactive(obj) {
  const dep = new Dep();
  return new Proxy(obj, {
    get(target, key) {
      if (Dep.target) {
        dep.addSub(Dep.target);
      }
      return Reflect.get(target, key);
    },
    set(target, key, value) {
      const result = Reflect.set(target, key, value);
      dep.notify();
      return result;
    }
  });
}

完整示例代码

将上述部分整合为一个完整的双向绑定实现:

class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}
Dep.target = null;

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];
    Dep.target = null;
    return value;
  }
  update() {
    const newValue = this.vm._data[this.exp];
    if (newValue !== this.value) {
      this.value = newValue;
      this.cb(newValue);
    }
  }
}

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();
    }
  });
}

function observe(obj) {
  if (!obj || typeof obj !== 'object') return;
  Object.keys(obj).forEach(key => {
    defineReactive(obj, key, obj[key]);
    observe(obj[key]);
  });
}

class Vue {
  constructor(options) {
    this._data = options.data;
    observe(this._data);
    this._compile(options.el);
  }
  _compile(el) {
    const element = document.querySelector(el);
    const nodes = element.children;
    Array.from(nodes).forEach(node => {
      if (node.hasAttribute('v-model')) {
        const exp = node.getAttribute('v-model');
        node.addEventListener('input', e => {
          this._data[exp] = e.target.value;
        });
        new Watcher(this, exp, val => {
          node.value = val;
        });
      }
    });
  }
}

// 使用示例
const vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
});

关键点总结

  • 数据劫持:通过 Object.definePropertyProxy 监听数据变化。
  • 依赖收集:在 getter 中收集依赖(Watcher),在 setter 中触发更新。
  • 发布-订阅模式:Dep 管理依赖,Watcher 订阅变化并执行回调。
  • 模板编译:解析模板中的指令(如 v-model),绑定事件和更新函数。

以上实现是一个简化版本,Vue 的实际实现更复杂,包括虚拟 DOM、组件系统等。

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

相关文章

vue实现事件绑定的是

vue实现事件绑定的是

Vue 事件绑定实现方式 Vue 通过 v-on 指令实现事件绑定,语法为 v-on:事件名="处理方法" 或简写为 @事件名="处理方法"。以下是具体实现方法和示例: 基础事件绑定 <…

vue实现双向

vue实现双向

Vue 实现双向绑定的方法 Vue 主要通过 v-model 指令实现双向绑定,适用于表单元素或自定义组件。以下是几种常见实现方式: 表单元素的双向绑定 在表单元素(如 input、textare…

php实现双向队列

php实现双向队列

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

vue如何实现绑定

vue如何实现绑定

数据绑定基础 Vue 通过 v-bind 指令实现动态绑定 HTML 属性或组件 prop。基本语法为 v-bind:属性名="表达式",简写为 :属性名="表达式"。例如绑定 class 或 sty…

vue双向绑定如何实现

vue双向绑定如何实现

Vue 双向绑定的实现原理 Vue 的双向绑定是通过数据劫持结合发布者-订阅者模式实现的,核心是 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)。…

.vue 双向绑定实现原理

.vue 双向绑定实现原理

Vue 双向绑定实现原理 Vue 的双向绑定核心是通过数据劫持结合发布-订阅模式实现的,主要依赖以下技术: 数据劫持(Object.defineProperty 或 Proxy) Vue 2.x…