refactor(reactivity): 重构响应式系统依赖收集机制

- 将依赖管理逻辑分离到独立的dep模块
- 创建Dep类型定义和createDep工厂函数
- 修改KeyToDepMap类型以使用Dep集合替代单个ReactiveEffect
- 添加trackEffects函数用于统一跟踪依赖
- 实现triggerEffects函数用于批量触发依赖
- 增加triggerEffect函数用于触发单个依赖
- 在effect运行时添加注释说明首次getter触发
- 添加HTML示例文件验证多依赖收集功能
This commit is contained in:
dj
2026-02-04 23:25:32 +08:00
parent 0850e05bd1
commit 668ecda610
3 changed files with 79 additions and 9 deletions

View File

@@ -0,0 +1,7 @@
import { ReactiveEffect } from './effect'
export type Dep = Set<ReactiveEffect>
export const createDep = (effects?: ReactiveEffect[]): Dep => {
const dep = new Set<ReactiveEffect>(effects) as Dep
return dep
}

View File

@@ -1,4 +1,7 @@
type KeyToDepMap = Map<any, ReactiveEffect>
import { createDep, Dep } from './dep'
import { isArray } from '@vue/shared'
type KeyToDepMap = Map<any, Dep>
/**
* 收集所有依赖的WeakMap实例
* key: 响应性对象
@@ -18,8 +21,9 @@ export let activeEffect: ReactiveEffect | undefined
export class ReactiveEffect<T = any> {
constructor(public fn: () => T) {}
run() {
//当前被激活的effect实例
activeEffect = this
return this.fn()
return this.fn() //完成第一次getter行为的触发
}
}
@@ -36,9 +40,20 @@ export function track(target: object, key: unknown) {
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
//为指定map, 指定key, 设置回调函数
depsMap.set(key, activeEffect)
console.log(targetMap)
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = createDep()))
}
trackEffects(dep)
}
/**
* 利用dep依次跟踪指定key的所有effect
*/
export function trackEffects(dep: Dep) {
dep.add(activeEffect!)
}
/**
@@ -53,10 +68,29 @@ export function trigger(target: object, key: unknown, newValue: unknown) {
return
}
//依据key, 从depsMap中取出value, 该value是一个ReactiveEffect类型的数据
const effect = depsMap.get(key) as ReactiveEffect
if (!effect) {
const dep: Dep | undefined = depsMap.get(key)
if (!dep) {
return
}
//执行effect中保存的fn函数
effect.fn()
triggerEffects(dep)
}
/**
* 依次触发dep中保存的依赖
* @param dep
*/
export function triggerEffects(dep: Dep) {
const effects = isArray(dep) ? dep : [...dep]
//依次触发依赖
for (const effect of effects) {
triggerEffect(effect)
}
}
/**
* 触发指定依赖
* @param effect
*/
export function triggerEffect(effect: ReactiveEffect) {
effect.run()
}

View File

@@ -0,0 +1,29 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
<script src="../../dist/vue.js"></script>
</head>
<body>
<div id="app">
<p id="p1"></p>
<p id="p2"></p>
</div>
</body>
<script>
const { reactive,effect } = Vue
const obj=reactive({
name:'张三'
})
effect(()=>{
document.querySelector('#p1').innerText=obj.name
})
effect(()=>{
document.querySelector('#p2').innerText=obj.name
})
setTimeout(()=>{
obj.name='李四'
},2000)
</script>
</html>