当前位置:首页 > 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 指令绑定事件监听器,语法为 v-on:event="handler" 或简写为 @ev…

vue实现动态绑定样式

vue实现动态绑定样式

动态绑定样式的方法 在Vue中实现动态绑定样式主要通过v-bind:class和v-bind:style指令完成,支持对象语法、数组语法等多种灵活方式。 对象语法 通过对象可以动态切换class,对…

vue实现双向绑定模拟

vue实现双向绑定模拟

Vue 双向绑定模拟实现 Vue 的双向绑定核心是通过数据劫持结合发布-订阅模式实现。以下是一个简化版的模拟实现: 数据劫持(Object.defineProperty) 通过 Object.def…

vue实现数据双向绑定

vue实现数据双向绑定

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

ie实现vue动态绑定

ie实现vue动态绑定

在IE浏览器中实现Vue动态绑定的方法 IE浏览器(尤其是IE11及以下版本)对ES6语法和现代JavaScript特性的支持有限,导致Vue.js的部分功能(如动态绑定)可能无法直接运行。以下是针对…

react如何绑定数据

react如何绑定数据

React 数据绑定的方法 React 中的数据绑定通常通过状态(state)和属性(props)来实现,以下是几种常见的绑定方式: 单向数据绑定(State to UI) React 默认采用单向…