响应式原理学习

Feb 23

这里只使用最核心的函数和最基本的实现, 不考虑边界条件

依赖收集

reactive

export function reactive(target: object) {
	// if trying to observe a readonly proxy, return the readonly version.
		if (isReadonly(target)) {
			return target
		}
		return createReactiveObject(
			target,
			false,
			mutableHandlers,
			mutableCollectionHandlers,
			reactiveMap
		)
}
export function reactive(target: object) {
	// if trying to observe a readonly proxy, return the readonly version.
		if (isReadonly(target)) {
			return target
		}
		return createReactiveObject(
			target,
			false,
			mutableHandlers,
			mutableCollectionHandlers,
			reactiveMap
		)
}

mutableHandlers

// mutableHandlers就是Proxy的配置
export const mutableHandlers: ProxyHandler<object> = {
	get,
	set,
	deleteProperty,
	has,
	ownKeys
}
// mutableHandlers就是Proxy的配置
export const mutableHandlers: ProxyHandler<object> = {
	get,
	set,
	deleteProperty,
	has,
	ownKeys
}

createReactiveObject

createReactiveObject作用就是创建代理对象

function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
...
	const proxy = new Proxy(
		target,
		// 这个就是上面的mutableHandlers
		baseHandlers
	)
	...
	return proxy
}
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
...
	const proxy = new Proxy(
		target,
		// 这个就是上面的mutableHandlers
		baseHandlers
	)
	...
	return proxy
}

mutableHandlers.get

就看get和set的实现, set在下面的依赖触发


JavaScript
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)
		}
		...
		return res
	}
}

JavaScript
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)
		}
		...
		return res
	}
}

track

export function track(target: object, type: TrackOpTypes, key: unknown) {
	if (shouldTrack && activeEffect) {
		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 function trackEffects(
		dep: Dep, // dep = new Set()
) {
	let shouldTrack = false
	shouldTrack = !dep.has(activeEffect!)
	if (shouldTrack) {
		dep.add(activeEffect!)
		// 双向收集 利于清理 后面会讲
		activeEffect!.deps.push(dep)
	}
}
export function track(target: object, type: TrackOpTypes, key: unknown) {
	if (shouldTrack && activeEffect) {
		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 function trackEffects(
		dep: Dep, // dep = new Set()
) {
	let shouldTrack = false
	shouldTrack = !dep.has(activeEffect!)
	if (shouldTrack) {
		dep.add(activeEffect!)
		// 双向收集 利于清理 后面会讲
		activeEffect!.deps.push(dep)
	}
}

首先以响应式对象为key存储到weakMap, value就是一个map对象, map对象以属性名作为key, value就是一个Set集合, 存取了这个属性所需要的effect, 可能有点绕, 举个例子就理解了

let product = reactive({price: 5, quantity: 2})
// targetMap里存储的就是这样一个结构
targetMap = new WeakMap([[{price: 5, quantity: 2}, new Map({
	['price', new Set()]
	['quantity', new Set()]
})]])

let product = reactive({price: 5, quantity: 2})
// targetMap里存储的就是这样一个结构
targetMap = new WeakMap([[{price: 5, quantity: 2}, new Map({
	['price', new Set()]
	['quantity', new Set()]
})]])


这样做是因为你的响应式对象不止product这一个, activeEffect就是当前正在执行的effect, 例如


let product = reactive({price: 5, quantity: 2})
effect(() => {
		product.price * product.quantity
})

let product = reactive({price: 5, quantity: 2})
effect(() => {
		product.price * product.quantity
})

当前的activeEffect就是这个正在执行的effect, 只有当你访问了这个对象的属性的时候才会走的track函数里, 例如

let product = reactive({price: 5, quantity: 2})
product.price
product['price']
Reflect.get(product, 'price')
let product = reactive({price: 5, quantity: 2})
product.price
product['price']
Reflect.get(product, 'price')

那是怎么执行track的呢, 看下面的effect

effect


export function effect<T = any>(
fn: () => T,
options?: ReactiveEffectOptions
): ReactiveEffectRunner {
	if ((fn as ReactiveEffectRunner).effect) {
		fn = (fn as ReactiveEffectRunner).effect.fn
	}
	// 重点是这个ReactiveEffect
	const _effect = new ReactiveEffect(fn)
	if (options) {
		extend(_effect, options)
		if (options.scope) recordEffectScope(_effect, options.scope)
	}
	if (!options || !options.lazy) {
		_effect.run()
	}
	const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
	runner.effect = _effect
	return runner
}

export function effect<T = any>(
fn: () => T,
options?: ReactiveEffectOptions
): ReactiveEffectRunner {
	if ((fn as ReactiveEffectRunner).effect) {
		fn = (fn as ReactiveEffectRunner).effect.fn
	}
	// 重点是这个ReactiveEffect
	const _effect = new ReactiveEffect(fn)
	if (options) {
		extend(_effect, options)
		if (options.scope) recordEffectScope(_effect, options.scope)
	}
	if (!options || !options.lazy) {
		_effect.run()
	}
	const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
	runner.effect = _effect
	return runner
}

effect函数很简单 就是判断是否立即执行你传入的callback也就是fn, 接下来重点看ReactiveEffect

export class ReactiveEffect<T = any> {
	active = true
	// 用来记录当前的activeEffect被哪些属性收集了, 主要是用来清理看按下面的cleanupEffect函数
	deps: Dep[] = []
	parent: ReactiveEffect | undefined = undefined
	constructor(
		public fn: () => T,
		public scheduler: EffectScheduler | null = null,
		scope?: EffectScope
	) {
		recordEffectScope(this, scope)
	}
	run() {
		if (!this.active) {
			return this.fn()
		}
		let parent: ReactiveEffect | undefined = activeEffect
		let lastShouldTrack = shouldTrack
		// parent用来处理嵌套 effect
		// 例如:
		// effect(() => { -> effect 1
		// state.name
		// effect(() => { -> effect 2
		// state.age
		// })
		// state.address
		// })
		//
		// 第一次取state上的name的时候,能收集到effect 1,然后取内层的state的age的时候,
		// age会收集到effect 2,然后内层effect的函数执行完后,
		// 我们就把activeEffect置为undefined了,那么最后取state上的 address的时候,
		// 它的get里拿到的activeEffect就是undefined了
		//
		// 利用parent解决
		// 我们在effect上都增加一个属性parent,最外层的effect上的parent就是undefined
		// 当我们执行下一个effect的时候,它的parent就是effect 1,age收集的就是effect 2
		// 当我们取state上的address的时候,再把this.parent赋值给activeEffect就可以了
		// 简而言之就是,当前自己的effect执行完了,就把自己的父节点赋值给activeEffect就解决了
		// effect 2 会将 effect 1 作为自己的 parent
		while (parent) {
		if (parent === this) {
			return
		}
		parent = parent.parent
		}
		try {
			this.parent = activeEffect
			activeEffect = this
			shouldTrack = true
			// 每次执行之前先清理
			// 举个例子
			// effect(() = {
			// flag ? state.name : state.age
			// })
			// 当这个effect执行的时候, flag可能会进行变化,当它变化的时候我们就需要收集另一个属性,而释放掉当前属性收集的effect
			//
			cleanupEffect(this)
			return this.fn()
		} finally {
			if (effectTrackDepth <= maxMarkerBits) {
				finalizeDepMarkers(this)
			}
			trackOpBit = 1 << --effectTrackDepth
			// 再还原回来
			activeEffect = this.parent
			shouldTrack = lastShouldTrack
			this.parent = undefined
			if (this.deferStop) {
				this.stop()
			}
		}
	}
	stop() {
		// stopped while running itself - defer the cleanup
		if (activeEffect === this) {
			this.deferStop = true
		} else if (this.active) {
			cleanupEffect(this)
		if (this.onStop) {
			this.onStop()
		}
		this.active = false
		}
	}
}
function cleanupEffect(effect: ReactiveEffect) {
	const { deps } = effect
	if (deps.length) {
		for (let i = 0; i < deps.length; i++) {
			deps[i].delete(effect)
		}
		deps.length = 0
	}
}

export class ReactiveEffect<T = any> {
	active = true
	// 用来记录当前的activeEffect被哪些属性收集了, 主要是用来清理看按下面的cleanupEffect函数
	deps: Dep[] = []
	parent: ReactiveEffect | undefined = undefined
	constructor(
		public fn: () => T,
		public scheduler: EffectScheduler | null = null,
		scope?: EffectScope
	) {
		recordEffectScope(this, scope)
	}
	run() {
		if (!this.active) {
			return this.fn()
		}
		let parent: ReactiveEffect | undefined = activeEffect
		let lastShouldTrack = shouldTrack
		// parent用来处理嵌套 effect
		// 例如:
		// effect(() => { -> effect 1
		// state.name
		// effect(() => { -> effect 2
		// state.age
		// })
		// state.address
		// })
		//
		// 第一次取state上的name的时候,能收集到effect 1,然后取内层的state的age的时候,
		// age会收集到effect 2,然后内层effect的函数执行完后,
		// 我们就把activeEffect置为undefined了,那么最后取state上的 address的时候,
		// 它的get里拿到的activeEffect就是undefined了
		//
		// 利用parent解决
		// 我们在effect上都增加一个属性parent,最外层的effect上的parent就是undefined
		// 当我们执行下一个effect的时候,它的parent就是effect 1,age收集的就是effect 2
		// 当我们取state上的address的时候,再把this.parent赋值给activeEffect就可以了
		// 简而言之就是,当前自己的effect执行完了,就把自己的父节点赋值给activeEffect就解决了
		// effect 2 会将 effect 1 作为自己的 parent
		while (parent) {
		if (parent === this) {
			return
		}
		parent = parent.parent
		}
		try {
			this.parent = activeEffect
			activeEffect = this
			shouldTrack = true
			// 每次执行之前先清理
			// 举个例子
			// effect(() = {
			// flag ? state.name : state.age
			// })
			// 当这个effect执行的时候, flag可能会进行变化,当它变化的时候我们就需要收集另一个属性,而释放掉当前属性收集的effect
			//
			cleanupEffect(this)
			return this.fn()
		} finally {
			if (effectTrackDepth <= maxMarkerBits) {
				finalizeDepMarkers(this)
			}
			trackOpBit = 1 << --effectTrackDepth
			// 再还原回来
			activeEffect = this.parent
			shouldTrack = lastShouldTrack
			this.parent = undefined
			if (this.deferStop) {
				this.stop()
			}
		}
	}
	stop() {
		// stopped while running itself - defer the cleanup
		if (activeEffect === this) {
			this.deferStop = true
		} else if (this.active) {
			cleanupEffect(this)
		if (this.onStop) {
			this.onStop()
		}
		this.active = false
		}
	}
}
function cleanupEffect(effect: ReactiveEffect) {
	const { deps } = effect
	if (deps.length) {
		for (let i = 0; i < deps.length; i++) {
			deps[i].delete(effect)
		}
		deps.length = 0
	}
}


实际上当effect执行的时候会执行传入给他的callback, 也就是上面的this.fn() 执行这个callback的时候就会出发product.price 和 product.quantity也就是访问了响应式对象的属性,我们设置了Proxy的getter代理, 在getter里就触发了track而此时activeEffect就是当前的effect.然后把activeEffect添加到dep里面 也就是这个属性的set集合, 这就是整个依赖收集的核心.

依赖触发

这个就相对简单了

看set函数

function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    ...
    const result = Reflect.set(target, key, value, receiver)
    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key)
    // 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
  }
}
function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    ...
    const result = Reflect.set(target, key, value, receiver)
    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key)
    // 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

trigger


export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  const depsMap = targetMap.get(target)
  if (!depsMap) {
    // never been tracked
    return
  }

  let deps: (Dep | undefined)[] = []

  if (key !== void 0) {
    deps.push(depsMap.get(key))
  }

  if (deps.length === 1) {
    if (deps[0]) {
        triggerEffects(deps[0])
    }
  } else {
    const effects: ReactiveEffect[] = []
    for (const dep of deps) {
      if (dep) {
        effects.push(...dep)
      }
    }
    triggerEffects(createDep(effects))
  }
}

export function triggerEffects(
  dep: Dep | ReactiveEffect[],
) {
  const effects = isArray(dep) ? dep : [...dep]
  for (const effect of effects) {
    if (effect.computed) {
      triggerEffect(effect)
    }
  }
  for (const effect of effects) {
    if (!effect.computed) {
      triggerEffect(effect)
    }
  }
}

function triggerEffect(
  effect: ReactiveEffect,
) {
   // 全局变量activeEffect保存着当前正在执行的effect 
   // 当前正在执行的effect不是自己的时候,我们在执行自己 避免死循环
  if (effect !== activeEffect) {
    effect.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)
  if (!depsMap) {
    // never been tracked
    return
  }

  let deps: (Dep | undefined)[] = []

  if (key !== void 0) {
    deps.push(depsMap.get(key))
  }

  if (deps.length === 1) {
    if (deps[0]) {
        triggerEffects(deps[0])
    }
  } else {
    const effects: ReactiveEffect[] = []
    for (const dep of deps) {
      if (dep) {
        effects.push(...dep)
      }
    }
    triggerEffects(createDep(effects))
  }
}

export function triggerEffects(
  dep: Dep | ReactiveEffect[],
) {
  const effects = isArray(dep) ? dep : [...dep]
  for (const effect of effects) {
    if (effect.computed) {
      triggerEffect(effect)
    }
  }
  for (const effect of effects) {
    if (!effect.computed) {
      triggerEffect(effect)
    }
  }
}

function triggerEffect(
  effect: ReactiveEffect,
) {
   // 全局变量activeEffect保存着当前正在执行的effect 
   // 当前正在执行的effect不是自己的时候,我们在执行自己 避免死循环
  if (effect !== activeEffect) {
    effect.run()
  }
}

完.

参考1

参考2

back