Reactive 和 Effect

Author Avatar
via
发表:2025-06-16 15:32:00
修改:2026-04-08 15:33:06

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.依赖清理

依赖清理的过程为

  1. 在每次执行effect函数前进行预清理,在执行完成后进行后清理

  2. 每次收集的时候判断当前的 dep和上一次同一个 effect的同索引位置的 dep

    1. 不相同把从 effectdeps中同索引替换成新的,然后把 dep里的这个 effect删掉

    2. 相同不处理

触发更新

遍历 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)  
    }  
}

评论