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

【Vue3源码解析】响应式原理

源码环境搭建

【Vue3源码解析】应用实例创建及页面渲染-CSDN博客

写文章时的Vue 版本:

"version": "3.5.13",

针对单个包进行开发环境打包、测试。

pnpm run dev reactivity

image-20250215180341644

image-20250215180401097

reactive 创建响应式对象

packages/reactivity/src/reactive.ts

export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (isReadonly(target)) {
    return target
  }
  /**
   * 通过调用 createReactiveObject 函数,创建一个响应式对象,并返回该对象。
   * 第一个参数是 目标对象
   * 第二个参数用来判断该对象是否是只读的
   * mutableHandlers 是一个对象,包含了响应式对象的一些操作方法,如get、set等。
   * mutableCollectionHandlers 是一个对象,包含了响应式集合对象的一些操作方法,如get、set等。
   * reactiveMap 是一个全局 WeakMap 对象,用于缓存 target 到 响应式对象的映射关系。
   */
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap,
  )
}
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>,
) {
  if (!isObject(target)) {
    if (__DEV__) {
      warn(
        `value cannot be made ${isReadonly ? 'readonly' : 'reactive'}: ${String(
          target,
        )}`,
      )
    }
    return target
  }
  // target is already a Proxy, return it.
  // exception: calling readonly() on a reactive object
  // 如果已经是 Proxy 代理对象,则直接返回
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  // 如果 ProxyMap 中有 target 对应的 proxy 对象 则直接返回
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // only specific value types can be observed.
  // 类型无效 则直接返回 target
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  // 创建 target 对应的 Proxy 代理对象 并传入 baseHandlers
  const proxy = new Proxy(
    target,
    // COLLECTION => Map/Set/WeakMap/WeakSet
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
  )
  proxyMap.set(target, proxy)
  return proxy
}

effect 和它的函数参数fn的作用

packages/reactivity/src/effect.ts

/**
 * 创建一个响应式副作用函数
 *
 * 此函数用于追踪函数内的响应式数据访问和修改,以便在数据变化时自动重新执行该函数
 * 它是响应式系统的核心部分,使得开发者可以轻松创建响应式的应用程序
 *
 * @param fn 要追踪的副作用函数,它会接收一个用于触发副作用的函数作为参数
 * @param options 可选的副作用选项,允许开发者自定义副作用的行为,例如是否应该停止追踪等
 * @returns 返回一个绑定了副作用函数的runner函数,用于手动触发副作用
 */
export function effect<T = any>(
  fn: () => T,
  // 可以由用户自定义,用于定义副作用effect的行为,例如shouldTrack、onTrack、onTrigger等
  // 这样可以覆盖ReactiveEffect的默认scheduler而执行自己的scheduler
  options?: ReactiveEffectOptions,
): ReactiveEffectRunner<T> {
  // 检查传入的函数是否已经是一个ReactiveEffect的副作用函数,如果是,则使用其内部的副作用函数
  if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect) {
    fn = (fn as ReactiveEffectRunner).effect.fn
  }
  // 调用一次effect函数,根据传入的fn创建一个新的ReactiveEffect实例对象:_effect
  // 一个fn 对应一个 _effect对象
  // fn 同时成为_effect 对象的fn属性(形成闭包)
  /**
   * effect(fn)
   * fn.effect => ReactiveEffect对象
   * ReactiveEffect对象.fn => fn
   */

  // 创建一个新的ReactiveEffect实例
  /**
   * 当内部执行scheduler的时候,它会回头调用effect的run,·而run方法内部会调用fn
   * 意味着:scheduler()=>run()=>fn()
   * 如何执行:那么之后我们如果想要重新执行fn函数,·只需要执行scheduler就可以了
   */
  const e = new ReactiveEffect(fn)

  // 如果提供了用户自定义的配置,则将配置合并到副作用实例中
  if (options) {
    extend(e, options)
  }

  // 尝试运行副作用函数,如果在执行过程中抛出错误,则停止追踪并重新抛出错误
  try {
    e.run()
  } catch (err) {
    e.stop()
    throw err
  }

  // 创建并返回一个副作用函数的runner,它绑定了当前的副作用实例
  const runner = e.run.bind(e) as ReactiveEffectRunner
  runner.effect = e

  // 返回副作用函数的runner,允许手动触发副作用 e.run()=> fn()
  return runner
}
/**
 * 执行当前的副作用函数
 *
 * 此函数负责执行副作用操作,并在执行前后处理一些清理和准备依赖项的工作
 * 它还管理副作用的执行状态,确保在执行过程中不会被中断,并在完成后清理依赖项
 *
 * @returns 返回副作用函数的执行结果
 */
run(): T {
  // TODO cleanupEffect

  // 检查副作用是否处于非激活状态,如果是,则直接执行副作用函数
  if (!(this.flags & EffectFlags.ACTIVE)) {
    // stopped during cleanup
    return this.fn()
  }

  // 设置当前副作用为正在运行状态
  this.flags |= EffectFlags.RUNNING
  // 执行清理上一次执行的副作用
  cleanupEffect(this)
  // 准备依赖项收集
  prepareDeps(this)
  // 保存当前正在执行的副作用和是否应该跟踪的全局状态
  const prevEffect = activeSub
  const prevShouldTrack = shouldTrack
  // 设置当前执行的副作用和全局跟踪状态
  activeSub = this
  shouldTrack = true

  try {
    // 执行副作用函数
    return this.fn()
  } finally {
    // 在开发环境下,确保当前执行的副作用被正确恢复
    if (__DEV__ && activeSub !== this) {
      warn(
        'Active effect was not restored correctly - ' +
          'this is likely a Vue internal bug.',
      )
    }
    // 清理当前执行的副作用的依赖项
    cleanupDeps(this)
    // 恢复之前保存的正在执行的副作用和是否应该跟踪的全局状态
    activeSub = prevEffect
    shouldTrack = prevShouldTrack
    // 清除当前副作用的正在运行状态
    this.flags &= ~EffectFlags.RUNNING
  }
}

执行fn后响应式变量依赖的收集和触发

Vue3.4 版本之前最后的一层,也就是 nameDepMap 这一层是 Set 结构,但是从 3.4 版本开始,变成了 Map ,用来记录 track 的 id,然而Vue3.5对该部分又进行了重构,不再记录id,只能说学的速度永远跟不上技术更新的速度~

image-20250215181003656

packages/reactivity/src/baseHandlers.ts

/**
 * BaseReactiveHandler 类实现了 ProxyHandler 接口,用于处理响应式对象的代理操作。
 * 它主要负责拦截和处理对响应式对象的访问和操作,根据是否只读和是否浅层次来决定如何处理这些操作。
 *
 * @param _isReadonly 是否只读,如果为 true,则该响应式对象不可被修改。
 * @param _isShallow 是否浅层次,如果为 true,则仅对外层属性进行响应式处理,不递归处理内部属性。
 */
class BaseReactiveHandler implements ProxyHandler<Target> {
  constructor(
    protected readonly _isReadonly = false,
    protected readonly _isShallow = false,
  ) {}

  /**
   * 拦截对目标对象属性的获取操作。
   * 此方法负责处理各种情况下的属性获取,包括处理特殊的 ReactiveFlags、数组的特殊情况、
   * 属性的追踪、以及返回值的处理等。
   *
   * @param target 目标对象,即被代理的响应式对象。
   * @param key 要访问的属性名。
   * @param receiver 代理对象或继承链上的其他对象。
   * @returns 属性值或处理后的值。
   */
  get(target: Target, key: string | symbol, receiver: object): any {
    // 处理特殊的 ReactiveFlags 属性
    if (key === ReactiveFlags.SKIP) return target[ReactiveFlags.SKIP]

    const isReadonly = this._isReadonly,
      isShallow = this._isShallow

    // 根据不同的 ReactiveFlags 返回相应的值
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    } else if (key === ReactiveFlags.IS_SHALLOW) {
      return isShallow
    } else if (key === ReactiveFlags.RAW) {
      if (
        receiver ===
          (isReadonly
            ? isShallow
              ? shallowReadonlyMap
              : readonlyMap
            : isShallow
              ? shallowReactiveMap
              : reactiveMap
          ).get(target) ||
        // receiver is not the reactive proxy, but has the same prototype
        // this means the receiver is a user proxy of the reactive proxy
        Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
      ) {
        return target
      }
      // early return undefined
      return
    }

    const targetIsArray = isArray(target)

    // 处理非只读情况下的特殊属性和方法
    if (!isReadonly) {
      let fn: Function | undefined
      if (targetIsArray && (fn = arrayInstrumentations[key])) {
        return fn
      }
      if (key === 'hasOwnProperty') {
        return hasOwnProperty
      }
    }

    // 获取属性值,考虑内置符号和非跟踪键的情况
    const res = Reflect.get(
      target,
      key,
      // if this is a proxy wrapping a ref, return methods using the raw ref
      // as receiver so that we don't have to call `toRaw` on the ref in all
      // its class methods
      isRef(target) ? target : receiver,
    )

    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res
    }

    // 在非只读情况下,跟踪属性的访问
    if (!isReadonly) {
      // 依赖追踪
      track(target, TrackOpTypes.GET, key)
    }

    // 根据是否浅层次和返回值的类型,决定是否需要进一步处理返回值
    if (isShallow) {
      return res
    }

    if (isRef(res)) {
      // ref unwrapping - skip unwrap for Array + integer key.
      return targetIsArray && isIntegerKey(key) ? res : res.value
    }

    if (isObject(res)) {
      // Convert returned value into a proxy as well. we do the isObject check
      // here to avoid invalid value warning. Also need to lazy access readonly
      // and reactive here to avoid circular dependency.
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }
}
class MutableReactiveHandler extends BaseReactiveHandler {
  constructor(isShallow = false) {
    super(false, isShallow)
  }

  /**
   * 设置一个响应式对象的属性。
   *
   * 该方法重写了 `Proxy` 对象的默认 `set` 行为,实现了自定义的属性设置逻辑。
   * 它处理了各种情况,例如浅层响应式、只读属性和 ref 解包,以确保响应式系统的正确行为。
   *
   * @param target - 被代理的原始对象。
   * @param key - 要设置的属性的名称或符号。
   * @param value - 属性的新值。
   * @param receiver - 代理对象本身。
   * @returns 返回设置属性的结果。
   */
  set(
    target: Record<string | symbol, unknown>,
    key: string | symbol,
    value: unknown,
    receiver: object,
  ): boolean {
  let oldValue = target[key]
  // 如果不是浅层响应式模式,需要进一步处理属性和值。
  if (!this._isShallow) {
    const isOldValueReadonly = isReadonly(oldValue)
    // 如果新旧值都不是浅层或只读的,将它们转换为原始状态。
    if (!isShallow(value) && !isReadonly(value)) {
      oldValue = toRaw(oldValue)
      value = toRaw(value)
    }
    // 如果目标不是数组且旧值是 ref 而新值不是 ref,需要特殊处理。
    if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
      // 如果旧值是只读的,属性不能被设置。
      if (isOldValueReadonly) {
        return false
      } else {
        // 否则,直接设置旧值 ref 的值。
        oldValue.value = value
        return true
      }
    }
  } else {
    // 在浅层模式下,对象按原样设置,无论是否是响应式的。
  }

  // 确定键是否已经存在。
  const hadKey =
    isArray(target) && isIntegerKey(key)
      ? Number(key) < target.length
      : hasOwn(target, key)
  // 使用 Reflect.set 尝试设置属性。
  const result = Reflect.set(
    target,
    key,
    value,
    isRef(target) ? target : receiver,
  )
  // 如果目标是原始对象本身,根据属性是新增还是更新触发相应的副作用。
  if (target === toRaw(receiver)) {
    if (!hadKey) {
      // 触发新增属性的副作用。
      trigger(target, TriggerOpTypes.ADD, key, value)
    } else if (hasChanged(value, oldValue)) {
      // 触发更新属性的副作用。
      trigger(target, TriggerOpTypes.SET, key, value, oldValue)
    }
  }
  return result
}


  deleteProperty(
    target: Record<string | symbol, unknown>,
    key: string | symbol,
  ): boolean {
    const hadKey = hasOwn(target, key)
    const oldValue = target[key]
    const result = Reflect.deleteProperty(target, key)
    if (result && hadKey) {
      trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
    }
    return result
  }

  has(target: Record<string | symbol, unknown>, key: string | symbol): boolean {
    const result = Reflect.has(target, key)
    if (!isSymbol(key) || !builtInSymbols.has(key)) {
      track(target, TrackOpTypes.HAS, key)
    }
    return result
  }

  ownKeys(target: Record<string | symbol, unknown>): (string | symbol)[] {
    track(
      target,
      TrackOpTypes.ITERATE,
      isArray(target) ? 'length' : ITERATE_KEY,
    )
    return Reflect.ownKeys(target)
  }
}

packages/reactivity/src/dep.ts

/**
 * 跟踪对响应式属性的访问。
 *
 * 该函数会检查当前正在运行的效果(effect),并将其记录为依赖项(dep),
 * 这些依赖项记录了所有依赖于该响应式属性的效果。
 *
 * @param target - 包含响应式属性的对象。
 * @param type - 定义对响应式属性的访问类型。
 * @param key - 要跟踪的响应式属性的标识符。
 */
export function track(target: object, type: TrackOpTypes, key: unknown): void {
  // 仅在启用了跟踪且存在活动效果时进行跟踪。activeSub在3.4版本及之前为activeEffect
  if (shouldTrack && activeSub) {
    // 获取目标对象的依赖映射,如果不存在则创建一个新的映射。
    let depsMap = targetMap.get(target)
    if (!depsMap) {
      targetMap.set(target, (depsMap = new Map()))
    }
    // 获取指定属性的依赖项,如果不存在则创建一个新的依赖项。
    let dep = depsMap.get(key)
    if (!dep) {
      depsMap.set(key, (dep = new Dep()))
      dep.map = depsMap
      dep.key = key
    }
    // 在开发模式下记录依赖项,并包含详细信息。
    if (__DEV__) {
      dep.track({
        target,
        type,
        key,
      })
    } else {
      dep.track()
    }
  }
}
/**
 * 查找与目标对象(或特定属性)关联的所有依赖项,并触发存储在其中的效果。
 *
 * @param target - 响应式对象。
 * @param type - 定义需要触发效果的操作类型。
 * @param key - 可用于指定目标对象中的特定响应式属性。
 * @param newValue - 新值,用于某些操作类型(如 SET)。
 * @param oldValue - 旧值,用于某些操作类型(如 SET)。
 * @param oldTarget - 旧的目标对象,用于某些操作类型(如 CLEAR)。
 */
export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>,
): void {
  const depsMap = targetMap.get(target)
  if (!depsMap) {
    // 从未被追踪过
    globalVersion++
    return
  }

  const run = (dep: Dep | undefined) => {
    if (dep) {
      if (__DEV__) {
        dep.trigger({
          target,
          type,
          key,
          newValue,
          oldValue,
          oldTarget,
        })
      } else {
        dep.trigger()
      }
    }
  }

  startBatch()

  if (type === TriggerOpTypes.CLEAR) {
    // 集合被清除
    // 触发目标对象的所有效果
    depsMap.forEach(run)
  } else {
    const targetIsArray = isArray(target)
    const isArrayIndex = targetIsArray && isIntegerKey(key)

    if (targetIsArray && key === 'length') {
      const newLength = Number(newValue)
      depsMap.forEach((dep, key) => {
        if (
          key === 'length' ||
          key === ARRAY_ITERATE_KEY ||
          (!isSymbol(key) && key >= newLength)
        ) {
          run(dep)
        }
      })
    } else {
      // 调度 SET | ADD | DELETE 的运行
      if (key !== void 0 || depsMap.has(void 0)) {
        run(depsMap.get(key))
      }

      // 调度 ARRAY_ITERATE 对于任何数字键的变化(长度已在上方处理)
      if (isArrayIndex) {
        run(depsMap.get(ARRAY_ITERATE_KEY))
      }

      // 也对 ADD | DELETE | Map.SET 运行迭代键
      switch (type) {
        case TriggerOpTypes.ADD:
          if (!targetIsArray) {
            run(depsMap.get(ITERATE_KEY))
            if (isMap(target)) {
              run(depsMap.get(MAP_KEY_ITERATE_KEY))
            }
          } else if (isArrayIndex) {
            // 新索引添加到数组 -> 长度变化
            run(depsMap.get('length'))
          }
          break
        case TriggerOpTypes.DELETE:
          if (!targetIsArray) {
            run(depsMap.get(ITERATE_KEY))
            if (isMap(target)) {
              run(depsMap.get(MAP_KEY_ITERATE_KEY))
            }
          }
          break
        case TriggerOpTypes.SET:
          if (isMap(target)) {
            run(depsMap.get(ITERATE_KEY))
          }
          break
      }
    }
  }

  endBatch()
}
ITERATE_KEY))
            }
          } else if (isArrayIndex) {
            // 新索引添加到数组 -> 长度变化
            run(depsMap.get('length'))
          }
          break
        case TriggerOpTypes.DELETE:
          if (!targetIsArray) {
            run(depsMap.get(ITERATE_KEY))
            if (isMap(target)) {
              run(depsMap.get(MAP_KEY_ITERATE_KEY))
            }
          }
          break
        case TriggerOpTypes.SET:
          if (isMap(target)) {
            run(depsMap.get(ITERATE_KEY))
          }
          break
      }
    }
  }

  endBatch()
}

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

相关文章:

  • 训练与优化
  • Python的那些事第二十二篇:基于 Python 的 Django 框架在 Web 开发中的应用研究
  • Java常见排序算法及代码实现
  • Spring Boot全局异常处理终极指南:从青铜到王者的实战演进
  • C语言中的常量与只读变量,#define与const的区别
  • 从养殖场到科技前沿:YOLOv11+OpenCV精准计数鸡蛋与鸡
  • FPGA的星辰大海
  • AI与前端安全:效率提升与安全保障并行
  • csghub安装(docker方式)
  • nginx通过location配置代理的原理和方式
  • IDEA的程序调试笔记
  • flutter 状态栏不显示 问题解决办法
  • Rust编程语言入门教程(二)hello_world
  • 开源模型应用落地-Qwen1.5-MoE-A2.7B-Chat与vllm实现推理加速的正确姿势(一)
  • Zookeeper(45) 如何在Zookeeper中删除节点?
  • 网络运维和网络安全哪个前景好?
  • Python数据可视化 - Matplotlib教程
  • Redis可视化连接工具RedisDesktopManager的下载与安装
  • 基于实例详解pytest钩子pytest_generate_tests动态生成测试的全过程
  • 数据结构与算法之排序算法-选择排序