Vue3 Reactivity

116 阅读3分钟

响应性基础API

graph TD
A[reactive]
B[readonly]
C[shallowReactive]
D[shallowReadonly]
E[createReactiveObject]
F(proxy)
A-->E
B-->E
C-->E
D-->E
E-->F
  1. reactive 返回对象的响应式副本
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}
  1. readonly 接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。
export function readonly<T extends object>(
  target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
  return createReactiveObject(
    target,
    true,
    readonlyHandlers,
    readonlyCollectionHandlers,
    readonlyMap
  )
}
  1. isProxy 检查对象是否是由 reactivereadonly 创建的 proxy。返回布尔值。
export function isProxy(value: unknown): boolean {
  return isReactive(value) || isReadonly(value)
}
  1. isReactive 检查对象是否是由 reactive 创建的响应式代理。返回布尔值。
export function isReactive(value: unknown): boolean {
  if (isReadonly(value)) {
    return isReactive((value as Target)[ReactiveFlags.RAW])
  }
  return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}
  1. isReadonly 检查对象是否是由 readonly 创建的只读代理。返回布尔值。
export function isReadonly(value: unknown): boolean {
  return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}
  1. toRaw 返回 reactivereadonly 代理的原始对象。
export function toRaw<T>(observed: T): T {
  return (
    (observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
  )
}
  1. markRaw 标记一个对象,使其永远不会转换为 proxy。返回对象本身。
export function markRaw<T extends object>(value: T): T {
  def(value, ReactiveFlags.SKIP, true)
  return value
}
  1. shallowReactive 创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。
export function shallowReactive<T extends object>(target: T): T {
  return createReactiveObject(
    target,
    false,
    shallowReactiveHandlers,
    shallowCollectionHandlers,
    shallowReactiveMap
  )
}
  1. shallowReadonly 创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)。
export function shallowReadonly<T extends object>(
  target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
  return createReactiveObject(
    target,
    true,
    shallowReadonlyHandlers,
    shallowReadonlyCollectionHandlers,
    shallowReadonlyMap
  )
}

Refs

  1. ref 接受一个内部值并返回一个响应式且可变的 ref 对象。

  2. unref 如果参数是一个 ref,则返回内部值,否则返回参数本身。

  3. toRef 可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

  4. toRefs 将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 propertyref

  5. isRef 检查值是否为一个 ref 对象。

  6. customRef 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。

  7. shallowRef 创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的。

  8. triggerRef 手动执行与 shallowRef 关联的任何副作用。

computed

  • 接受一个 getter 函数,并为从 getter 返回的值返回一个不变的响应式 ref 对象。
  • 或者,它也可以使用具有 get 和 set 函数的对象来创建可写的 ref 对象。
export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>

  if (isFunction(getterOrOptions)) {
    getter = getterOrOptions
    setter = __DEV__
      ? () => {
          console.warn('Write operation failed: computed value is readonly')
        }
      : NOOP
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }

  return new ComputedRefImpl(
    getter,
    setter,
    isFunction(getterOrOptions) || !getterOrOptions.set
  ) as any
}

class ComputedRefImpl<T> {
  private _value!: T
  private _dirty = true

  public readonly effect: ReactiveEffect<T>

  public readonly __v_isRef = true;
  public readonly [ReactiveFlags.IS_READONLY]: boolean

  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean
  ) {
    this.effect = effect(getter, {
      lazy: true,
      scheduler: () => {
        if (!this._dirty) {
          this._dirty = true
          trigger(toRaw(this), TriggerOpTypes.SET, 'value')
        }
      }
    })

    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    // the computed ref may get wrapped by other proxies e.g. readonly() #3376
    const self = toRaw(this)
    if (self._dirty) {
      self._value = this.effect()
      self._dirty = false
    }
    track(self, TrackOpTypes.GET, 'value')
    return self._value
  }

  set value(newValue: T) {
    this._setter(newValue)
  }
}

watchEffect

  • 在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。

watch

  • watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生更改时被调用。
  • 与 watchEffect 比较,watch 允许我们:
    • 惰性地执行副作用;
    • 更具体地说明应触发侦听器重新运行的状态;
    • 访问被侦听状态的先前值和当前值