vue 双向绑定原理

响应式原理

Vue的核心特性之一,数据驱动视图,修改了数据之后视图随之响应更新。
Vue2是借助Object.defineProperty()实现的,而Vue3是借助proxy实现的,Proxy相比Object.defineProperty在处理数组和新增属性的响应式处理上更加方便。

双向绑定

Vue2的双向绑定是通过数据劫持结合发布/订阅模式实现的。数据响应系统的核心是观察者模式(Observer)订阅者(Dep)模式。

1. 数据劫持

Vue 通过递归遍历数据对象的所有属性,并使用Object.defineProperty方法将每个属性转换为getter/setter形式。这使得当数据属性被访问或修改时,可以触发相应的回调函数。

实现步骤:

  1. 创建Observer对象:当一个Vue实例被创建时,它会调用new Observer(value)来将数据对象转换为响应式。Observer类负责将对象的每个属性都转换为getter/setter。
  2. 定义getter和setter:对于每个属性,Observer会在属性的Object.defineProperty中定义gettersetter
  • getter:当访问属性时,getter会记录当前的依赖关系。
  • setter:当属性被修改时,setter会通知所有的依赖者更新。
  1. 递归处理嵌套属性:如果属性值是一个对象,那么会递归调用Observer类来继续转换内部属性。

2.发布/订阅模式

Vue使用了发布/订阅模式来管理依赖关系。每个数据属性都有一个对应的Dep对象,它负责收集所有依赖该属性的观察者(Watcher)

实现步骤:

  1. 创建Dep对象:当Observer创建getter时,会为每个属性创建一个Dep实例。
  2. 收集依赖:当渲染视图时,Watcher会自动收集依赖。这是通过在渲染过程中调用Dep.target来实现。
  3. 更新依赖:当属性值发生变化时,Dep会通知所有相关的Watcher更新。

3.Watcher对象

Watcher对象负责更新视图。每当一个属性被访问时,就会创建一个Watcher实例。当属性发生变化时,Watcher就会被Dep通知,从而触发更新视图的操作。

实现步骤:

  1. 创建Watcher实例:在渲染过程中,每当一个属性被访问,就会创建一个新的Watcher实例。
  2. 收集依赖:Watcher会自动收集依赖,也就是将自身添加到Dep的依赖列表中。
  3. 更新视图:当属性值发生变化时,Watcher会收到通知并执行更新逻辑。

示例代码

function observe(value) {
  if (!value || typeof value !== 'object') {
    return;
  }

  let ob = new Observer(value);
}

function Observer(value) {
  this.value = value;
  this.walk(value);
}

Observer.prototype.walk = function (obj) {
  const keys = Object.keys(obj);

  for (let i = 0; i < keys.length; i++) {
    defineReactive(obj, keys[i]);
  }
};

function defineReactive(obj, key) {
  let val = obj[key];
  observe(val); // 递归处理嵌套属性

  const dep = new Dep();

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      if (Dep.target) {
        dep.addSub(Dep.target);
      }
      return val;
    },
    set: function reactiveSetter(newVal) {
      if (newVal === val) {
        return;
      }
      val = newVal;
      observe(newVal); // 递归处理新赋值
      dep.notify(); // 通知依赖更新
    }
  });
}

function Dep() {
  this.subs = [];
}

Dep.prototype.addSub = function (sub) {
  this.subs.push(sub);
};

Dep.prototype.notify = function () {
  this.subs.forEach(sub => sub.update());
};

// Watcher 示例
function Watcher(vm, expression, callback) {
  this.vm = vm;
  this.expression = expression;
  this.callback = callback;

  Dep.target = this;
  this.value = this.get();
  Dep.target = null;
}

Watcher.prototype.get = function () {
  return this.vm[this.expression];
};

Watcher.prototype.update = function () {
  const newValue = this.vm[this.expression];
  const oldValue = this.value;

  if (newValue !== oldValue) {
    this.callback(newValue, oldValue);
  }
};
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值