Vue3 的响应式系统源码级执行流程解析
字数 388 2025-11-06 12:41:12
Vue3 的响应式系统源码级执行流程解析
题目描述:深入分析 Vue3 响应式系统从初始化到触发更新的完整源码级执行流程,包括依赖收集、触发更新等核心环节的具体实现细节。
解题过程:
1. 响应式对象创建阶段
// reactive() 函数入口
function reactive(target) {
return createReactiveObject(target, false, mutableHandlers)
}
function createReactiveObject(target, isReadonly, baseHandlers) {
// 1. 检查是否已经是响应式对象
if (target.__v_raw) return target
// 2. 使用 Proxy 创建响应式代理
const proxy = new Proxy(target, baseHandlers)
// 3. 建立原始对象与代理对象的映射关系
reactiveMap.set(target, proxy)
return proxy
}
2. 依赖收集的核心实现
// mutableHandlers 中的 get 拦截器
const get = createGetter()
function createGetter() {
return function get(target, key, receiver) {
// 1. 获取原始值
const res = Reflect.get(target, key, receiver)
// 2. 执行依赖收集(核心)
track(target, "get", key)
// 3. 如果值是对象,递归响应化
if (isObject(res)) {
return reactive(res)
}
return res
}
}
// track 函数实现依赖收集
function track(target, type, key) {
// 1. 获取当前正在执行的副作用函数
let activeEffect = effectStack[effectStack.length - 1]
if (!activeEffect) return
// 2. 获取 target 对应的依赖映射
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
// 3. 获取 key 对应的依赖集合
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
// 4. 建立双向关联
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
activeEffect.deps.push(dep) // 副作用函数记录自己属于哪些依赖集合
}
}
3. 副作用函数(effect)注册机制
function effect(fn, options = {}) {
// 1. 创建副作用函数
const effect = createReactiveEffect(fn, options)
// 2. 如果不是懒执行,立即执行一次进行初始依赖收集
if (!options.lazy) {
effect()
}
return effect
}
function createReactiveEffect(fn, options) {
const effect = function reactiveEffect() {
// 防止递归调用
if (!effectStack.includes(effect)) {
try {
// 1. 压入栈顶,设置为当前活跃的 effect
effectStack.push(effect)
activeEffect = effect
// 2. 执行原始函数,触发依赖收集
return fn()
} finally {
// 3. 执行完成,弹出栈
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
}
}
}
// 记录相关元信息
effect.deps = [] // 该 effect 所属的所有依赖集合
effect.options = options
return effect
}
4. 触发更新的完整流程
// set 拦截器实现
const set = createSetter()
function createSetter() {
return function set(target, key, value, receiver) {
// 1. 获取旧值,判断操作类型(新增/修改)
const oldValue = target[key]
const hadKey = hasOwn(target, key)
// 2. 执行反射设置
const result = Reflect.set(target, key, value, receiver)
// 3. 触发更新(核心)
if (!hadKey) {
trigger(target, "add", key, value)
} else if (value !== oldValue) {
trigger(target, "set", key, value, oldValue)
}
return result
}
}
// trigger 函数实现更新触发
function trigger(target, type, key, newValue, oldValue) {
// 1. 获取 target 对应的所有依赖
const depsMap = targetMap.get(target)
if (!depsMap) return
// 2. 创建需要执行的 effect 集合(避免重复和递归)
const effects = new Set()
const add = (effectsToAdd) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect !== activeEffect) { // 避免当前正在执行的 effect
effects.add(effect)
}
})
}
}
// 3. 根据操作类型收集相关依赖
if (key !== void 0) {
add(depsMap.get(key)) // 该 key 对应的依赖
}
// 4. 数组特殊处理
if (type === 'add' && isArray(target)) {
add(depsMap.get('length')) // 数组新增影响 length
}
// 5. 执行所有收集到的 effect
const run = (effect) => {
if (effect.options.scheduler) {
effect.options.scheduler(effect) // 使用调度器
} else {
effect() // 直接执行
}
}
effects.forEach(run)
}
5. 调度器(scheduler)与批量更新
// Vue 组件更新使用的调度器
const scheduler = (effect) => {
// 1. 将 effect 加入队列,准备批量更新
if (!queue.includes(effect)) {
queue.push(effect)
// 2. 使用微任务延迟执行,实现批量更新
if (!isFlushing) {
nextTick(flushJobs)
}
}
}
function flushJobs() {
isFlushing = true
// 排序保证:父组件在子组件之前更新
queue.sort((a, b) => a.id - b.id)
// 批量执行所有 effect
for (let i = 0; i < queue.length; i++) {
const effect = queue[i]
effect()
}
// 清空队列
queue.length = 0
isFlushing = false
}
关键执行流程总结:
- 初始化阶段:Proxy 拦截器设置,建立响应式对象
- 依赖收集阶段:effect 执行时通过 getter 建立 target→key→effect 的映射关系
- 更新触发阶段:setter 通过映射关系找到对应 effect,通过调度器批量执行
- 清理阶段:effect 重新执行前清理旧依赖,避免无效更新
这个流程确保了 Vue3 响应式系统的高效性和精确性,是 Vue 组件更新的核心基础。