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

vue2双向绑定解析

Vue 2 的双向绑定原理基于 Object.defineProperty,核心源码在 src/core/observer 目录中。

1. 核心模块:observer

observer 模块负责将普通对象转换为响应式对象,主要包括以下文件:

  • index.js:定义 Observer 类,用于将对象转换为响应式。
  • dep.js:定义 Dep 类,用于管理依赖(订阅者)。
  • watcher.js:定义 Watcher 类,用于监听数据变化并触发更新。

2. Observer 类

Observer 类是 Vue 2 响应式系统的核心,它通过 Object.defineProperty 将对象的属性转换为 getter 和 setter,从而实现依赖收集和派发更新。

源码位置:src/core/observer/index.js

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number;

  constructor(value: any) {
    this.value = value;
    this.dep = new Dep();
    this.vmCount = 0;
    def(value, '__ob__', this); // 将 Observer 实例挂载到对象的 __ob__ 属性上
    if (Array.isArray(value)) {
      // 处理数组
      if (hasProto) {
        protoAugment(value, arrayMethods);
      } else {
        copyAugment(value, arrayMethods, arrayKeys);
      }
      this.observeArray(value);
    } else {
      // 处理对象
      this.walk(value);
    }
  }

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

  observeArray(items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i]);
    }
  }
}
  • Observer 类会递归地将对象的属性转换为响应式。
  • 对于数组,Vue 2 通过重写数组的变异方法(如 pushpop 等)来实现响应式。

3. defineReactive 函数

defineReactive 是 Vue 2 实现响应式的核心函数,它通过 Object.defineProperty 定义属性的 getter 和 setter

源码位置:src/core/observer/index.js

export function defineReactive(
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep(); // 每个属性都有一个 Dep 实例,用于管理依赖

  const getter = property && property.get;
  const setter = property && property.set;
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key];
  }

  let childOb = !shallow && observe(val); // 递归处理嵌套对象
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend(); // 收集依赖
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value;
    },
    set: function reactiveSetter(newVal) {
      const value = getter ? getter.call(obj) : val;
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return;
      }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = !shallow && observe(newVal); // 对新值进行响应式处理
      dep.notify(); // 通知依赖更新
    },
  });
}
  • getter:在访问属性时,调用 dep.depend() 收集依赖。
  • setter:在修改属性时,调用 dep.notify() 通知依赖更新。

4. Dep 类

Dep 类是依赖管理器,用于存储和管理 Watcher 实例。

源码位置:src/core/observer/dep.js

export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor() {
    this.id = uid++;
    this.subs = [];
  }

  addSub(sub: Watcher) {
    this.subs.push(sub);
  }

  removeSub(sub: Watcher) {
    remove(this.subs, sub);
  }

  depend() {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
  }

  notify() {
    const subs = this.subs.slice();
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update();
    }
  }
}
  • Dep.target:当前正在计算的 Watcher 实例。
  • subs:存储所有订阅了该属性的 Watcher 实例。
  • notify:通知所有订阅者更新。

5. Watcher 类

Watcher 类是 Vue 2 中用于监听数据变化的订阅者,它会在数据变化时触发回调函数。

源码位置:src/core/observer/watcher.js

export default class Watcher {
  vm: Component;
  expression: string;
  cb: Function;
  id: number;
  deps: Array<Dep>;
  newDeps: Array<Dep>;
  depIds: SimpleSet;
  newDepIds: SimpleSet;
  getter: Function;
  value: any;

  constructor(
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    this.vm = vm;
    this.cb = cb;
    this.id = ++uid; // uid for batching
    this.deps = [];
    this.newDeps = [];
    this.depIds = new Set();
    this.newDepIds = new Set();
    this.getter = parsePath(expOrFn);
    this.value = this.get();
  }

  get() {
    pushTarget(this); // 将当前 Watcher 设置为 Dep.target
    let value;
    const vm = this.vm;
    try {
      value = this.getter.call(vm, vm);
    } catch (e) {
      // 处理错误
    } finally {
      popTarget(); // 恢复之前的 Watcher
      this.cleanupDeps();
    }
    return value;
  }

  update() {
    queueWatcher(this); // 将 Watcher 加入队列,等待批量更新
  }

  run() {
    const value = this.get();
    if (value !== this.value || isObject(value)) {
      const oldValue = this.value;
      this.value = value;
      this.cb.call(this.vm, value, oldValue); // 执行回调
    }
  }
}
  • Watcher 实例会在初始化时调用 get 方法,触发属性的 getter,从而收集依赖。
  • 当数据变化时,Watcher 的 update 方法会被调用,最终触发回调函数。

Vue 2 的双向绑定原理基于 Object.defineProperty,通过以下步骤实现:

  1. 响应式化Observer 类将对象的属性转换为 getter 和 setter
  2. 依赖收集:在 getter 中调用 dep.depend(),将当前 Watcher 添加到依赖列表中。
  3. 派发更新:在 setter 中调用 dep.notify(),通知所有依赖的 Watcher 更新。
  4. 批量更新Watcher 的更新会被加入队列,异步执行,以提高性能。

相关文章:

  • 单片机设计暖脚器研究
  • 投资晚报 3.12
  • 【论文笔记】FLARE:feed-forward+posegeometry estimate+GS
  • 调优案例一:堆空间扩容提升吞吐量实战记录
  • 适合二次开发的Web组态软件推荐
  • 子母钟系统,京准电子科技助力高考精准计时
  • 机器学习常见激活函数
  • Vitis IDE 艰难切换--从传统 Vitis GUI 到 2024.1 统一软件界面
  • NetAssist 5.0.14网络助手基础使用及自动应答使用方案
  • 【学习笔记】《逆向工程核心原理》03.abex‘crackme-2、函数的调用约定、视频讲座-Tut.ReverseMe1
  • ESP8266-调试
  • 代理(Delegate)、闭包(Closure)、Notification(通知中心) 和 swift_event_bus适用场景和工作方式
  • vue3实现虚拟滚动Vue-Virtual-Scroller
  • docker2
  • 2.angular指令
  • 根据指定 Excel 模板将 Excel 明细数据生成新的 Excel 文档
  • JVM垃圾收集器合集
  • 考研复试c语言常见问答题汇总2
  • Git Fast-forward 合并详解:原理、场景与最佳实践
  • 【Ubuntu系统设置固定内网ip,且不影响访问外网 】
  • 澳大利亚联邦选举投票正式开始
  • 德国斯图加特发生车辆冲撞人群事件,至少三人受伤
  • 从“土”到“潮”,唢呐何以“圈粉”年轻人
  • 5月资金面前瞻:政府债净融资规模预计显著抬升,央行有望提供流动性支持
  • 两部门发布“五一”假期全国森林草原火险形势预测
  • Meta一季度净利增长三成:上调全年资本支出,受关税影响亚洲出口电商广告支出减少