function set( target: any, key: string | symbol, value: any, receiver: any ): boolean { value = toRaw(value) const hadKey = hasOwn(target, key) const oldValue = target[key] // 如果旧的值是ref,而新的值不是ref if (isRef(oldValue) && !isRef(value)) { // 直接更改原始ref即可 oldValue.value = value return true } const result = Reflect.set(target, key, value, receiver) // don't trigger if target is something up in the prototype chain of original if (target === toRaw(receiver)) { /* istanbul ignore else */ if (__DEV__) { const extraInfo = { oldValue, newValue: value } if (!hadKey) { trigger(target, OperationTypes.ADD, key, extraInfo) } else if (value !== oldValue) { trigger(target, OperationTypes.SET, key, extraInfo) } } else { if (!hadKey) { trigger(target, OperationTypes.ADD, key) } else if (value !== oldValue) { trigger(target, OperationTypes.SET, key) } } } return result }
判断旧值是ref,新值不是ref
// 这种情况 const val = ref(0) const state = reative({ count: val }) state.count = 1 // 其实state.count最终还是ref,还是能通过value访问 state.count.value // 1
调用Reflect.set修改值
开发环境下,拿到新旧值组成的对象,调用trigger,为什么开发环境要这么做呢,其实是为了方便onTrigger能拿到新旧值
trigger(target, OperationTypes.ADD, key, extraInfo)
可以看到第二个参数和track是一样的enum,有两种情况,一种我们设置了新的属性和值,另一种修改了原有属性值,下面我们来看看trigger实现。
trigger
export function trigger( target: any, type: OperationTypes, key?: string | symbol, extraInfo?: any ) { const depsMap = targetMap.get(target) if (depsMap === void 0) { // never been tracked return } // effect set const effects: Set<ReactiveEffect> = new Set() // computed effect set const computedRunners: Set<ReactiveEffect> = new Set() if (type === OperationTypes.CLEAR) { depsMap.forEach(dep => { addRunners(effects, computedRunners, dep) }) } else { // 添加effect到set中 if (key !== void 0) { addRunners(effects, computedRunners, depsMap.get(key as string | symbol)) } // also run for iteration key on ADD | DELETE if (type === OperationTypes.ADD || type === OperationTypes.DELETE) { const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY addRunners(effects, computedRunners, depsMap.get(iterationKey)) } } // 执行set中的effect const run = (effect: ReactiveEffect) => { scheduleRun(effect, target, type, key, extraInfo) } computedRunners.forEach(run) effects.forEach(run) }
看到这个函数开始的targetMap,大家应该很清楚要干嘛了吧,没错,拿到对象的Map,它包含了属性的所有依赖
如果没有Map直接返回
创建了两个Set,要干嘛用呢
// 用来保存将要执行的依赖 const effects: Set<ReactiveEffect> = new Set() // computed依赖,因为trigger不仅是要处理effect,watch,还要处理computed惰性求值的情况 const computedRunners: Set<ReactiveEffect> = new Set()
处理三种情况CLEAR,ADD,DELETE,SET(这里没有标识)
// effect set const effects: Set<ReactiveEffect> = new Set() // computed effect set const computedRunners: Set<ReactiveEffect> = new Set() function addRunners( effects: Set<ReactiveEffect>, computedRunners: Set<ReactiveEffect>, effectsToAdd: Set<ReactiveEffect> | undefined ) { if (effectsToAdd !== void 0) { effectsToAdd.forEach(effect => { if (effect.computed) { computedRunners.add(effect) } else { effects.add(effect) } }) } }
可以看到,三种情况实际上都差不多,唯一的区别就是,如果添加的对象是数组,就会拿到length属性的依赖,用于修改数组长度
if (type === OperationTypes.ADD || type === OperationTypes.DELETE) { const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY addRunners(effects, computedRunners, depsMap.get(iterationKey)) }
执行属性对应的依赖
// 执行set中的effect const run = (effect: ReactiveEffect) => { scheduleRun(effect, target, type, key, extraInfo) } computedRunners.forEach(run) effects.forEach(run)