一、Vue Ref 基本使用

ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回:

1const count = ref(0)
2
3console.log(count) // { value: 0 }
4console.log(count.value) // 0
5
6count.value++
7console.log(count.value) // 1

 

二、Vue Reactive 的局限性

  • 有限的值类型:不能用于原始类型
  • 不能替换整个对象:不正确赋值会丢失响应性
  • 对解构操作不友好:解构也会丢失响应性
const state = reactive(true); // Incorrect
1let state = reactive({ count: 0 })
2state = xxxxxx; // Responsive loss
1const state = reactive({ count: 0 })
2let { count } = state; // Responsive loss

 

三、Vue Ref 实现原理

Vue Ref 是通过定义对象的属性访问器,即 getter 和 setter 方法来拦截对象属性的 get 和 set 操作,实现依赖追踪和更新

查看 Vue 3.4 源码 (packages/reactivity/src/ref.ts):

1export function ref<T>(value: T): Ref<UnwrapRef<T>>
2export function ref<T = any>(): Ref<T | undefined>
3export function ref(value?: unknown) {
4  return createRef(value, false)
5}
6
7function createRef(rawValue: unknown, shallow: boolean) {
8  if (isRef(rawValue)) {
9    return rawValue
10  }
11  return new RefImpl(rawValue, shallow)
12}

 

get 操作的核心方法 trackRefValue 实现依赖追踪,set  操作的核心方法 triggerRefValue 实现触发更新

1class RefImpl<T> {
2  private _value: T
3  private _rawValue: T
4
5  public dep?: Dep = undefined
6  public readonly __v_isRef = true
7
8  constructor(
9    value: T,
10    public readonly __v_isShallow: boolean,
11  ) {
12    this._rawValue = __v_isShallow ? value : toRaw(value)
13    this._value = __v_isShallow ? value : toReactive(value)
14  }
15
16  get value() {
17    trackRefValue(this)
18    return this._value
19  }
20
21  set value(newVal) {
22    /// ...
23    if (hasChanged(newVal, this._rawValue)) {
24      /// ...
25      triggerRefValue(this, DirtyLevels.Dirty, newVal, oldVal)
26    }
27  }
28}

 

trackRefValue → trackEffect,可以发现收集依赖最终使用的 trackEffect 方法和 Vue Reactive 一样,说明该 effect 类是响应式的核心: 

1export function trackRefValue(ref: RefBase<any>) {
2  if (shouldTrack && activeEffect) {
3    ref = toRaw(ref)
4    trackEffect(
5      activeEffect,
6      (ref.dep ??= createDep(
7        () => (ref.dep = undefined),
8        ref instanceof ComputedRefImpl ? ref : undefined,
9      )),
10      __DEV__
11        ? {
12            target: ref,
13            type: TrackOpTypes.GET,
14            key: 'value',
15          }
16        : void 0,
17    )
18  }
19}

 

triggerRefValue → triggerEffects,可以发现依赖更新最终使用的 triggerEffects 方法和 Vue Reactive 一样,同上:

1export function triggerRefValue(
2  ref: RefBase<any>,
3  dirtyLevel: DirtyLevels = DirtyLevels.Dirty,
4  newVal?: any,
5  oldVal?: any,
6) {
7  ref = toRaw(ref)
8  const dep = ref.dep
9  if (dep) {
10    triggerEffects(
11      dep,
12      dirtyLevel,
13      __DEV__
14        ? {
15            target: ref,
16            type: TriggerOpTypes.SET,
17            key: 'value',
18            newValue: newVal,
19            oldValue: oldVal,
20          }
21        : void 0,
22    )
23  }
24}

 

trackEffect 和 triggerEffects 使用的 effect 类 → ReactiveEffect (packages/reactivity/src/effect.ts) 具体实现如下(只显示核心部分):

deps 属性:存储依赖

run 方法:运行的核心方法

1export type Dep = Map<ReactiveEffect, number> & {
2  cleanup: () => void
3  computed?: ComputedRefImpl<any>
4}
5
6export class ReactiveEffect<T = any> {
7  /// ...
8  deps: Dep[] = []
9  /// ...
10
11  constructor(
12    public fn: () => T,
13    public trigger: () => void,
14    public scheduler?: EffectScheduler,
15    scope?: EffectScope,
16  ) {
17    /// ...
18  }
19
20  /// ...
21
22  run() {
23    /// ...
24    try {
25      /// ...
26      return this.fn()
27    } finally {
28      /// ...
29    }
30  }
31
32  /// ...
33}

 

四、其他

学习的记录,仅供参考