function scheduleRun( effect: ReactiveEffect, target: any, type: OperationTypes, key: string | symbol | undefined, extraInfo: any ) { if (__DEV__ && effect.onTrigger) { effect.onTrigger( extend( { effect, target, key, type }, extraInfo // { oldValue, newValue: value } ) ) } if (effect.scheduler !== void 0) { effect.scheduler(effect) } else { effect() } }
最后调用了scheduleRun,它内部会分别执行onTrigger,scheduler,effect
需要注意的是,只有开发环境才会执行onTrigger,这也是为什么,前面要这么判断
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) } }
readonly
有了前面的基础,readonly看起来会非常简单,唯一的区别就是rawToReadonly,rawToReadonly, readonlyHandlers
export function readonly(target: object) { if (reactiveToRaw.has(target)) { target = reactiveToRaw.get(target) } return createReactiveObject( target, rawToReadonly, readonlyToRaw, readonlyHandlers, readonlyCollectionHandlers ) }
前两个大家应该能猜出来了,关键是最后这个readonlyHandlers,区别就在set
set(target: any, key: string | symbol, value: any, receiver: any): boolean { if (LOCKED) { if (__DEV__) { console.warn( `Set operation on key "${key as any}" failed: target is readonly.`, target ) } return true } else { return set(target, key, value, receiver) } }
它的实现很简单,不过LOCKED有是什么鬼,大家可以找到lock.ts
//vue-next\packages\reactivity\src\lock.ts export let LOCKED = true export function lock() { LOCKED = true } export function unlock() { LOCKED = false }
看似简单,但是却非常重要,它能够控制被readonly的对象能够暂时被更改,就比如我们常用的props,它是无法被修改的,但是Vue内部又要对他进行更新,那怎么办,话不多说,我们再源码中看他具体应用
// vue-next\packages\runtime-core\src\componentProps.ts export function resolveProps( instance: ComponentInternalInstance, rawProps: any, _options: ComponentPropsOptions | void ) { const hasDeclaredProps = _options != null const options = normalizePropsOptions(_options) as NormalizedPropsOptions if (!rawProps && !hasDeclaredProps) { return } const props: any = {} let attrs: any = void 0 const propsProxy = instance.propsProxy const setProp = propsProxy ? (key: string, val: any) => { props[key] = val propsProxy[key] = val } : (key: string, val: any) => { props[key] = val } unlock() // 省略一些修改props操作。。 lock() instance.props = __DEV__ ? readonly(props) : props instance.attrs = options ? __DEV__ && attrs != null ? readonly(attrs) : attrs : instance.props }
这里前后分别调用了unlock和lock,这样就可以控制对readonly属性的修改
那么readonly的讲解就到这了
computed
export function computed<T>( getterOrOptions: (() => T) | WritableComputedOptions<T> ): any { const isReadonly = isFunction(getterOrOptions) const getter = isReadonly ? (getterOrOptions as (() => T)) : (getterOrOptions as WritableComputedOptions<T>).get const setter = isReadonly ? null : (getterOrOptions as WritableComputedOptions<T>).set let dirty: boolean = true let value: any = undefined const runner = effect(getter, { lazy: true, computed: true, scheduler: () => { dirty = true } }) return { _isRef: true, // expose effect so computed can be stopped effect: runner, get value() { if (dirty) { value = runner() dirty = false } trackChildRun(runner) return value }, set value(newValue) { if (setter) { setter(newValue) } else { // TODO warn attempting to mutate readonly computed value } } } }
首先是前面这段
const isReadonly = isFunction(getterOrOptions) const getter = isReadonly ? (getterOrOptions as (() => T)) : (getterOrOptions as WritableComputedOptions<T>).get const setter = isReadonly ? null : (getterOrOptions as WritableComputedOptions<T>).set
大家都知道computed是可以单独写一个函数,或者get,set访问的,这里不多讲
然后调用了effect,这里lazy设置为true, scheduler可以更改dirty为true
const runner = effect(getter, { lazy: true, computed: true, scheduler: () => { dirty = true } })
然后我们具体来看看,返回的对象