vue 3.2.26 源码解读(一)reactivity响应式
vue 3.2.26 源码解读(二)初始化渲染
vue 3.2.26 源码解读(三)diff算法原理
总体流程图
demo
带着两个问题来看下面这段代码
- reactive的返回值是什么?
- effect怎么感知到内部响应式数据的变化?
test('base reactive', ()=>{
const a = {
bbb: 1
};
const ar = reactive(a)
let dummy
effect(() => (
dummy = ar.bbb
))
expect(dummy).toBe(1)
ar.bbb = 2;
expect(dummy).toBe(2)
})
带着这两个问题我们来看代码中的核心逻辑
reactive 方法
- 判断是否只读
- 调用
createReactiveObject返回响应式对象
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
createReactiveObject
- 校验代理元对象是否合法
- 使用全局proxyMap保存代理对象,以便下次调用
- 使用Proxy代理
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
//... 校验逻辑逻辑
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
以上代理逻辑主要写在baseHandlers中代理普通对象,我们先按照顺序一步一步看代码是怎么执行的。
effect 方法
- 创建
ReactiveEffect - 判断是有
options配置且是否lazy决定是否首次调用ReactiveEffect中的run方法 - 指定
run方法中的this指向
export function effect<T = any>(
fn: () => T,
options?: ReactiveEffectOptions
): ReactiveEffectRunner {
//... 校验逻辑省略
const _effect = new ReactiveEffect(fn)
//... 非主线逻辑省略
if (!options || !options.lazy) {
_effect.run()
}
const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
runner.effect = _effect
return runner
}
ReactiveEffect
- 构造函数
constructor中传入三个参数fnscheduler,scope, 我们重点关注第一个参数
const effectStack: ReactiveEffect[] = []
let activeEffect: ReactiveEffect | undefined
export class ReactiveEffect<T = any> {
active = true
deps: Dep[] = []
// can be attached after creation
computed?: boolean
allowRecurse?: boolean
onStop?: () => void
// dev only
onTrack?: (event: DebuggerEvent) => void
// dev only
onTrigger?: (event: DebuggerEvent) => void
constructor(
public fn: () => T,
public scheduler: EffectScheduler | null = null,
scope?: EffectScope | null
) {
recordEffectScope(this, scope)
}
run() {
try {
effectStack.push((activeEffect = this)
//...
return this.fn()
} finally {
//...
effectStack.pop()
const n = effectStack.length
activeEffect = n > 0 ? effectStack[n - 1] : undefined
}
}
stop() {
if (this.active) {
cleanupEffect(this)
if (this.onStop) {
this.onStop()
}
this.active = false
}
}
}
run方法中核心逻辑:
- 将当前
effect赋值给activeEffect并推入effectStack - 执行
fn,即用户传入的函数。 - 重置
activeEffect,effectStack状态
当我们执行fn时,我们会触发ar对象的get操作,接下来我们来看看这里做了什么
effect(() => (
dummy = ar.bbb
))
baseHandlers get
- 获取当前
key对应的原始对象值 - 跟踪当前
key的依赖 - 判断原始值是否为对象,是否需返回
reactive
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
//...
const res = Reflect.get(target, key, receiver)
//...
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
//...
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
track依赖跟踪
- 创建一个依赖对照表
- 每个
obj对应一个依赖WeekMap - 对象中每个
key对应一个Set Set中存储当前key对应的effect
const targetMap = new WeakMap<any, KeyToDepMap>()
export function track(target: object, type: TrackOpTypes, key: unknown) {
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = createDep()))
}
trackEffects(dep)
}
export const createDep = (effects?: ReactiveEffect[]): Dep => {
const dep = new Set<ReactiveEffect>(effects) as Dep
dep.w = 0
dep.n = 0
return dep
}
export function trackEffects(
dep: Dep,
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
dep.add(activeEffect!)
activeEffect!.deps.push(dep)
}
以上依赖收集过程结束,接下来我们再来看看当我们更新时的逻辑
baseHandlers set
- 比对新老值,判断是否新增属性
- 若是修改,判断新老值是否一致,走不同的
trigger逻辑
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
//...
let oldValue = (target as any)[key]
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)) {
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
trigger触发更新
- 获取当前依赖
Map - 根据
key从Map上获取对应的effects - 遍历执行
effects,判断effects上是否有scheduler,若有则执行scheduler否则执行run方法。
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
const depsMap = targetMap.get(target)
//...
let deps: (Dep | undefined)[] = []
//...
deps.push(depsMap.get(key))
//...
triggerEffects(deps[0])
}
export function triggerEffects(
dep: Dep | ReactiveEffect[],
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
// spread into array for stabilization
for (const effect of isArray(dep) ? dep : [...dep]) {
if (effect !== activeEffect || effect.allowRecurse) {
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
}
}
}
最后我们再来回顾这张图看看有没有清晰很多: