# Vue 3.x

# 源码学习进度

# 响应式原理 reactivity

  • reactive.ts
    • 常规 Object、Array 的reactive()/readonly()/shallowReactive()
      • get
      • set
      • deleteProperty
      • has
      • ownKeys
    • isReactive()
    • isReadonly()
    • isProxy()
    • toRaw():递归地获取代理对象observed的目标对象
    • markRaw():标记value是非代理对象
    • 【TODO】Map、Set、WeakMap、WeakSet
  • 【done】baseHandlers.ts
  • 【done】effect.ts
    • effect()
    • track()
    • trigger()
  • 【done】ref.ts
    • ref()
    • shallowRef()
    • isRef()
    • unref(): 获取ref的原始值
    • toRef(): 解决因提前触发值的get方法(比如解构赋值、扩展运算符)导致的响应式丢失问题
    • toRefs(): 基于toRef()的封装,处理整个对象
    • triggerRef(): 手动触发依赖refeffect重新运行
    • customRef: 自定义refget/set函数
    • 【TODO】proxyRefs
  • 【done】computed.ts
    • computed()是基于effect()的封装,不同点有
      • 首次声明computed()时不进行计算
      • 即使computed的依赖项发生了变化也不立即重新计算,而是在外部再次获取computed时才重新计算
      • computed具有双重角色,既能成为别的effect的依赖项,也能自己作为effect依赖外部的依赖项

# runtime-core

# watchEffect/watch

  • watchEffect/watch
    • 首次都会运行一次
    • 调用watchEffect()的返回值stop,可以停止副作用,且将该watcher从当前 Vue 实例instance.effects数组里移除
  • watchEffect(effect, options?)
  • watch(source, callback, options?)
    • source的依赖项改变导致source重新计算后,则满足以下条件之一,回调函数callback会运行
      • 第一个参数是ref,或是reactive的对象,或是指定了deep: true
      • 通过第一个参数获取的值改变了

# 为什么解构赋值、扩展运算符等会造成响应式丢失

const obj = reactive({ foo: 1 }) // obj 是响应式数据
const obj2 = { foo: obj.foo }

effect(() => {
  console.log(obj2.foo) // 这里读取 obj2.foo
})

obj.foo = 2  // 设置 obj.foo 显然无效
1
2
3
4
5
6
7
8

2行获取obj.foo时会触发obj.fooget函数进行依赖收集,但是此时activeEffect为空,这导致obj.foo没有被任何的effect所依赖,且赋值结束后,obj2的值为{ foo: 1 }obj.foo的值是原始值,而不是响应式的,因此在effect()函数执行时不会将obj.foo收集为依赖项,因此最后一行给obj.foo赋值时不会触发effect()的重新计算。

解构赋值、扩展运算符也是同理,都是先将值获取到再赋值给变量,而这个过程中activeEffect都为空,不会进行依赖收集,而赋值后的变量不是响应式的。

本站总访问量    次