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

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 的更新会被加入队列,异步执行,以提高性能。


http://www.kler.cn/a/584586.html

相关文章:

  • 【 <一> 炼丹初探:JavaWeb 的起源与基础】之 JavaWeb 中的文件上传与下载:实现文件管理功能
  • STM32 HAL库实战:轻松实现串口通信驱动蓝牙模块与ESP8266开发
  • 数据中心安全建设方案,数据安全运营体系建设方案,信息中心安全解决方案(PPT)
  • 深度学习基础--CNN经典网络之“DenseNet”简介,源码研究与复现(pytorch)
  • RabbitMQ五种消息模型
  • 零成本搭建Calibre个人数字图书馆支持EPUB MOBI格式远程直读
  • 鸿蒙开发-一多开发之媒体查询功能
  • XSS跨站脚本攻击
  • Bash和Zsh的主要差异是?
  • 新鲜速递:OpenAI-Agents-Python:构建智能代理系统的轻量级框架
  • Docker子网冲突解决方案及配置说明
  • 第十五届蓝桥杯C/C++ C 组全部题目详细题解
  • 微信小程序使用的SSL证书在哪里申请?
  • 兼职招聘平台(源码+文档+讲解+演示)
  • tomcat负载均衡配置
  • leetcode0056. 合并区间 - medium
  • SQL Server性能优化实战:从瓶颈定位到高效调优
  • R+VIC模型融合实践技术应用及未来气候变化模型预测
  • Oracle获取SQL执行日志
  • 深度学习基础-onnxruntime推理模型