reactive Get
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
...
指定特殊key值的响应
...
...
对于Array与Symbol的的处理
...
// 依赖收集(后面细说)
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
// 深层响应式转换,shallowReactive与shallowReadonly
if (shallow) {
return res
}
// 如果访问的参数是ref,返回res.value
// 这也就是
// const count = ref(1)
// const obj = reactive({})
// obj.count = count
// obj.count === count.value
//的原因
if (isRef(res)) {
const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
return shouldUnwrap ? res.value : res
}
// res 是个对象或者数组类型,则递归执行 reactive 函数把 res 变成响应式
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
指定特殊key值
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (
key === ReactiveFlags.RAW &&
receiver === (isReadonly ? readonlyMap : reactiveMap).get(target)
) {
return target
}
IS_REACTIVE与IS_READONLY,判断是 reactive 还是 readonly 响应 RAW,从对应全局Map中的获取对应的reactive或readonly
对于Array与Symbol的的处理
const targetIsArray = isArray(target)
if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
//对提前指定的key值进行了处理
//'includes', 'indexOf', 'lastIndexOf'
//'push', 'pop', 'shift', 'unshift', 'splice'
return Reflect.get(arrayInstrumentations, key, receiver)
}
//获取值
const res = Reflect.get(target, key, receiver)
//内置 Symbol key 不需要依赖收集
if (
isSymbol(key)
? builtInSymbols.has(key as symbol)
: isNonTrackableKeys(key)
) {
return res
}
arrayInstrumentations
const arrayInstrumentations: Record<string, Function> = {}
;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
const method = Array.prototype[key] as any
arrayInstrumentations[key] = function(this: unknown[], ...args: unknown[]) {
//看上面指定特殊key值,可得arr就是得到的是传入reactive的target
//toRaw=(observed) => (observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
const arr = toRaw(this)
//遍历数组的子集,给每个元素都进行依赖收集
for (let i = 0, l = this.length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '')
}
// 先尝试用参数本身,可能是响应式数据
const res = method.apply(arr, args)
if (res === -1 || res === false) {
// 如果失败,再尝试把参数转成原始数据
return method.apply(arr, args.map(toRaw))
} else {
return res
}
}
})
//直接执行返回结果就行,这里的变化可以被proxy监听到就无需想vue2.x中的收集依赖了
;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
const method = Array.prototype[key] as any
arrayInstrumentations[key] = function(this: unknown[], ...args: unknown[]) {
const res = method.apply(this, args)
return res
}
})
可以看到在includes, indexOf, lastIndexOf中做了除了调用代理函数外,还做了依赖收集
reactive Set
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
//获取oldValue
let oldValue = (target as any)[key]
//这里如果oldValue是ref,但value不是一个ref。赋值给oldValue.value
if (!shallow) {
value = toRaw(value)
oldValue = toRaw(oldValue)
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
} else {
// in shallow mode, objects are set as-is regardless of reactive or not
}
//target是数组情况下,Number(key)是否小于target的长度
//target是对象情况下,key是否存在target中
//hadKey:key是否存在target中
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
const result = Reflect.set(target, key, value, receiver)
// trigger 函数派发通知
if (target === toRaw(receiver)) {
if (!hadKey) {
//key不存在target中,使用ADD标记
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
//key存在target中,使用SET标记
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
reactive中ref元素的修改
const a = ref(1);
const b = reactive({a});
b.a = 2;
console.log(a.value, b.a); => 2 2
a.vlaue = 3;
console.log(a.value, b.a); => 3 3
b.a = ref(233);
console.log(a.value, b.a);=> 2 233
value = toRaw(value)
oldValue = toRaw(oldValue)
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
oldValue是ref,value不是ref。对oldValue的value赋值,也就能确保同时修改ref
track
const a = reactive({ b : 1 })
console.log(a.b)
//target:a
//key:b
//type:'get'
function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) {
return
}
// targetMap是全局变量,所有的 target 都在其中
// 查询a是否在targetMap中
// 是否应该收集依赖
let shouldTrack = true
// 当前激活的 effect
let activeEffect
// 原始数据对象 map
const targetMap = new WeakMap()
// depsMap用于保存a的key值,一个Map
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
//dep用于保存使用这个key的dep依赖,一个Set确保唯一值
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
if (!dep.has(activeEffect)) {
// 收集当前激活的 effect 作为依赖
dep.add(activeEffect)
// 当前激活的 effect 收集 dep 集合作为依赖
activeEffect.deps.push(dep)
}
}
graph TD
targetMap --> depsMap --> dep
a --> b --> b的deps
元素触发get时收集依赖,同时通知已经收集的dep集合
target
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
//track依赖收集时获得的target,没有就不用触发派发事件
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
//运行的 effects 集合
const effects = new Set<ReactiveEffect>()
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect !== activeEffect || effect.allowRecurse) {
effects.add(effect)
}
})
}
}
//收集需要通知的依赖
if (type === TriggerOpTypes.CLEAR) {
//clear清除操作
//搜集target下所有key的dep
depsMap.forEach(add)
} else if (key === 'length' && isArray(target)) {
//数组的length值修改
//搜集length和key值大于newValue的dep
depsMap.forEach((dep, key) => {
if (key === 'length' || key >= (newValue as number)) {
add(dep)
}
})
} else {
//key不为undefined
//收集指定 key 的dep
if (key !== void 0) {
add(depsMap.get(key))
}
}
//执行搜集到的所有 dep
const run = (effect: ReactiveEffect) => {
if (effect.options.scheduler) {
effect.options.scheduler(effect)
} else {
effect()
}
}
effects.forEach(run)
}
target有两步。1,根据操作搜集对应的依赖 2,触发搜集到的依赖
track | target |
---|---|
搜集依赖 | 派送触发 |
targer[targetMap] => targetMap[key] => depSet | 根据操作搜集depSet,统一触发 |
下一章将副作用函数effect