看源码时候做的笔记-----------
1.ref是什么?
from:vue-next/packages/reactivity/src/ref.ts
示例:
import { ref, ref } from 'vue'; let a: Ref<String> = ref('hello world');
let b: Ref<Object> = ref({name: 'hello world'}); console.log(b)
生成的变量:
首先变量a的类型是 Ref<String>,这个类型 Ref的定义如下,包含了 value,_shallow,[RefSymbol]。我们从上图看,ref变量里面还有 __v_isRef、_rawValue、_value、_shallow
declare const RefSymbol: unique symbol export interface Ref<T = any> { value: T /** * Type differentiator only. * We need this to be in public d.ts but don't want it to show up in IDE * autocomplete, so we use a private Symbol instead. */ [RefSymbol]: true /** * @internal */ _shallow?: boolean }
再看看 ref 的实现:
(ts的函数重载: https://jkchao.github.io/typescript-book-chinese/typings/functions.html#%E9%87%8D%E8%BD%BD
export function ref<T extends object>(value: T): ToRef<T> export function ref<T>(value: T): Ref<UnwrapRef<T>> export function ref<T = any>(): Ref<T | undefined> export function ref(value?: unknown) { return createRef(value) } function createRef(rawValue: unknown, shallow = false) { if (isRef(rawValue)) { return rawValue }
// RefImpl 实例会初始化 __v_isRef、_rawValue、_value、_shallow
// __v_isRef 用在isRef func中判断,当前变量是不是ref
return new RefImpl(rawValue, shallow)
}
RefImpl在构造ref的时候 ,会判断用户传入的_rawValue是不是object,不是object就直接赋值给_value, 是object就 convert(_rawValue)
const convert = <T extends unknown>(val: T): T =>
isObject(val) ? reactive(val) : val
可以看出来,最后将object类型的变量,构造为reactive的proxy。
2.reactive是什么?
from:vue-next/packages/reactivity/src/reactive.ts
响应性基础API:https://www.vue3js.cn/docs/zh/api/basic-reactivity.html#reactive
2.1.reactiveMap 和 readonlyMap 为什么是 weakMap,WeakMap 和 Map有什么区别?
Map 是由 key 数组和 value 数组构成,遍历时,先遍历 key, 找到 index , 然后再从 value 数组取值。两个很大的缺点:
a) 赋值和搜索操作都是 O(n) 的时间复杂度( n 是键值对的个数),因为这两个操作都需要遍历全部整个数组来进行匹配。
b) 是可能会导致内存泄漏,因为数组会一直引用着每个键和值。这种引用使得垃圾回收算法不能回收处理他们,即使没有其他任何引用存在了。
WeakMap
对象是一组键/值对的集合,其中的键是弱引用的,因为是弱引用,所以key不可被枚举,如果key 是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。其键必须是对象,而值可以是任意的。
a) WeakMap 持有的是每个键对象的“弱引用”,而弱引用的对象,垃圾回收机制不考虑对该对象的引用,这意味着在没有其他引用存在时垃圾回收能正确进行。
原生 WeakMap 的结构是特殊且有效的,其用于映射的 key 只有在其没有被回收时才是有效的。
effec.ts 中,Dep是个set,KeyToDepMap 是个 Map,targetMap 也是个WeakMap。
2.2.Reflect 和 Object 有什么区别?
2.3 怎么定义reactive数据?
可以创建reactive、shallowReactive、readonly、shallowReadonly四种不同的reactive数据类型,具体解释见:https://www.vue3js.cn/docs/zh/api/basic-reactivity.html#markraw
每种reactive类型对应不同的createHandler,分别是mutableHandlers、shallowReactiveHandlers、readonlyHandlers、shallowReadonlyHandlers,
创建过程中,需要检测teaget是否是readonly、是否已经是reactive、是否已经被标记为raw类型、是否是基础类型不可扩展、是否已经存在于map中,并做相应处理。
未创建并且满足创建条件的,将target转换为proxy:
baseHandlers 和 collectionHandlers,负责给proxy传入不同的劫持handler。
2.4 怎么创建reactive数据类型?
那 collectionHandlers(适用于Map/WeakMap/Set/WeakSet类型的target)和 baseHandlers (适用于Object和Array类型的target)有什么区别?
2.4.1:baseHandlers:vue-next/packages/reactivity/src/baseHandlers.ts
其实baseHandler主要是为proxy提供了handler:
reactiveVar = new Proxy( {}, { get, set, has, deleteProperty, ownKeys } ) = new Proxy({},handler)
在baseHandlers中,createGetter 负责创建get/shallowGet/readonlyGet/shallowReadonlyGet,区分在key是symbol、readonly、shallow、ref等情况下key的返回值,根绝情况确认是否要track追踪收集依赖。
createGetter(isReadonly = false, shallow = false) {
// 处理 __v_isReactive、__v_isReadonly、__v_raw 情况
// 处理 数组取值
// 处理 key 是symbol的情况
// track
// 处理shallow和ref的情况
// 处理 res(Reflect.get(target, key, receiver))是object的情况,递归处理nested varabiles
}
createSetter 负责创建 set/shallowSet,(readonly不需要set)
function set( target: object, key: string | symbol, value: unknown, receiver: object): boolean { // 处理 非shallow非数组且isRef的情况,直接赋值 // 处理 数组复制的情况 Reflect.set(target, key, value, receiver), trigger }
2.4.2:collectionHandlers:vue-next/packages/reactivity/src/collectionHandlers.ts
因为 collectionHandler 处理的对象类型的特殊性,key是object,所以 collectionHandlers 给proxy提供的handler类型很多。
总归来说,就是将各种数据类型分为了三类,分别处理,生成proxy,将数据置为reactive
最后,可以实践一下,看看reactive的产物,有什么区别?
const wm1 = new WeakMap(); const o1 = {}, o2 = function(){}, o3 = window; wm1.set(o1, 37); wm1.set(o2, "azerty"); const ws = new WeakSet(); const foo = {}; const bar = {}; ws.add(foo); ws.add(bar); const testCOMMONobject = reactive({ a: 1, b: 'test', }); console.log(testCOMMONobject); const testCOMMONarr = reactive(['测试数组', '测试数组1', '测试数组2']) console.log(testCOMMONarr) const testCOLLECTIONwm = reactive(wm1); console.log(testCOLLECTIONwm) const testCOLLECTIONws = reactive(ws); console.log(testCOLLECTIONws) console.log(readonly(testCOMMONobject)); console.log(markRaw(testCOMMONarr)) // __v_skip: true
reactive的原理就是生成proxy劫持,在setter时候trigger,在trigger中收集目标并触发effects,最终触发patch。
3.trigger和track是怎么生效的?
import { track, trigger } from './effect';
from: vue-next/packages/reactivity/src/effect.ts
3.1.哪些操作operations会触发track和trigger?
当对一个对象进行get、has、iterate的时候,会触发该对象的track,收集依赖到targetMap。对对象进行set、add、delete、clear时会触发trigger,使用target中的deps触发依赖追踪。
3.2.怎么收集保存对象的依赖信息?
我们来看源码,源码先声明了一个targetMap来保存对象的依赖信息:
(不得不说,vue3 也是一本 typescript 高级教程😂
// The main WeakMap that stores {target -> key -> dep} connections. // Conceptually, it's easier to think of a dependency as a Dep class // which maintains a Set of subscribers, but we simply store them as // raw Sets to reduce memory overhead. type Dep = Set<ReactiveEffect> type KeyToDepMap = Map<any, Dep> const targetMap = new WeakMap<any, KeyToDepMap>() export interface ReactiveEffect<T = any> {
// 限制这个接口的实现必须是一个返回值为T(泛型)的函数,https://forum.vuejs.org/t/vue3-0-ts-t/81276
(): T
_isEffect: true id: number active: boolean raw: () => T deps: Array<Dep> options: ReactiveEffectOptions allowRecurse: boolean }
3.3.track
track(obj, TrackOpTypes.GET,'a');
运行示例,就可以看到target的数据结构:
const obj = { a: '1', } const targetMap = new WeakMap(); const depsMap = new Map() targetMap.set(obj, depsMap); let dep = depsMap.get('a') if (!dep) { depsMap.set('a', (dep = new Set())) } let effect = {}; effect.id = 2; effect.allowRecurse = true; effect._isEffect = true; effect.active = true; effect.raw = () => {}; effect.deps = []; effect.options = {}; dep.add(effect); effect.deps.push(dep); console.log(targetMap)
我们通过一个实例:
const obj = { a: '1', } track(obj, TrackOpTypes.GET, 'a');
看看track的运行过程,看看track是如何收集obj.a的依赖的:
export function track(target: object, type: TrackOpTypes, key: unknown) { if (!shouldTrack || activeEffect === undefined) { return } // 查询 targetMap 中是否保存有 obj 的依赖,obj是否被追踪 let depsMap = targetMap.get(target) // 如果 depsMap 不存在,targetMap.set(obj, new Map()),添加obj的依赖追踪 if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } // 查询targetMap的 key -> obj 中是否有 'a'的依赖追踪,如果没有添加 let dep = depsMap.get(key) if (!dep) { // depsMap.set('a', new Set()) depsMap.set(key, (dep = new Set())) } // 如果没有 activeEffect,新增 if (!dep.has(activeEffect)) { dep.add(activeEffect) activeEffect.deps.push(dep) if (__DEV__ && activeEffect.options.onTrack) { activeEffect.options.onTrack({ effect: activeEffect, target, type, key }) } } }
3.4 trigger
trigger的运行过程。其实是消费targetMap的依赖,找到依赖后,开始执行任务。
const obj = { a: '1', } trigger(obj, TriggerOpTypes.SET, 'a', '2', '1');