Reactive 和 Effect
Reactive
一. 方法
1.createReactiveObject
使用 createReactiveObject 创建Proxy 对象 来创建响应式对应。
import { isObject } from '@vue/shared'
import { mutableHandlers, ReactiveFlags } from './baseHandler'
const reavtiveMap = new WeakMap()
function createReactiveObject(target) {
// 统一做判断,响应式对象必须是对象才可以
if (!isObject(target)) {
return target
}
// 如果是响应式的,直接返回
if (target[ReactiveFlags.IS_REACTIVE]) {
return target
}
// mutableHandlers:用来处理 get 和 set
let proxy = new Proxy(target, mutableHandlers)
// 取缓存,如果有则直接返回
const exitsProxy = reavtiveMap.get(target)
if (exitsProxy) {
return exitsProxy
}
// 存进缓存
reavtiveMap.set(target, proxy)
return proxy
}
export function reactive(target) {
return createReactiveObject(target)
}
2.mutableHandlers
在 get 的时候进行依赖收集,在 set 的时候进行触发更新
import { activeEffect } from './effect'
import { track, trigger } from './effectAcive'
export enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive',
}
// proxy 需要搭配 reflect 来使用
export const mutableHandlers: ProxyHandler<any> = {
get(target, key, receiver) {
if (key === ReactiveFlags.IS_REACTIVE) {
return true
}
// 取值的时候 让响应式属性和 effect 映射起来
// 依赖收集
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
// 找到属性 让对应的effect重新执行
let oldValue = target[key]
let result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
// 需要触发更新
trigger(target, key, value, oldValue)
return result
}
return result
},
}
依赖收集
Vue 使用了构建一个WeakMap,进行收集依赖。
这个 Map 构造的格式为:
{
监听的对象: {
监听对象的key值: {
ReactiveEffect: ReactiveEffect._trackId
},
监听对象的key值: {
ReactiveEffect: ReactiveEffect._trackId
},
}
}
1.track
对应的代码片段:
export const createDep = (cleanup, key) => {
const dep = new Map() as any
dep.cleanup = cleanup
dep.name = key
return dep
}
function cleanDepEffect(dep, effect) {
dep.delete(effect)
if (dep.size === 0) {
dep.cleanup()
}
}
export const trackEffect = (effect, dep) => {
if (dep.get(effect) !== effect._trackId) {
dep.set(effect, effect._trackId)
let oldDep = effect.deps[effect._depsLength]
if (oldDep !== dep) {
if (oldDep) {
// 删除掉老的
cleanDepEffect(oldDep, effect)
}
// 换成新的
effect.deps[effect._depsLength++] = dep
} else {
effect._depsLength++
}
}
}
export function track(target, key) {
// activeEffect 有这个属性,说明这个 key是在effect 中访问的,没有说明在 effect 之外不用处理
if (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(() => depsMap.delete(key), key)))
}
// 将当前 effect 放入dep 中,后续可以根据值的变化触发此 dep中存放的 effect
trackEffect(activeEffect, dep)
}
}
依赖收集中首先构造了上面说的 map 结构,其次做了一些优化:
依赖清理
2.依赖清理
依赖清理的过程为
在每次执行
effect函数前进行预清理,在执行完成后进行后清理每次收集的时候判断当前的
dep和上一次同一个effect的同索引位置的dep不相同把从
effect的deps中同索引替换成新的,然后把dep里的这个effect删掉相同不处理
触发更新
遍历 WeakMap,执行 key 对应的 effrct
执行调用了ReactiveEffect的第二个参数 scheduler,它运行了 run 方法.
export const triggerEffect = (dep) => {
for (const effect of dep.keys()) {
if (effect.scheduler) {
effect.scheduler()
}
}
}
export function trigger(target, key, value, oldValue) {
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
const dep: Map<any, any> = depsMap.get(key)
if (dep) {
triggerEffect(dep)
}
}