Vue3 源码面试问题:副作用函数与 Trigger 触发更新

问题

Trigger 触发更新:在 set 或 deleteProperty 拦截器中调用 trigger 函数,通知所有依赖该属性的副作用函数重新执行,这其中的副作用是不是指的组件的 render 函数执行生成虚拟 dom?

解答

在 Vue3 的响应式系统中,副作用函数(effect) 是一个更广泛的概念,组件的 render 函数只是其中一种常见类型。让我详细解释:

副作用函数的定义

副作用函数是指:

  • 依赖响应式数据的函数
  • 依赖的响应式数据变化时,需要自动重新执行的函数

常见的副作用函数类型

  1. 组件的渲染函数(render)

    • 这是最常见的副作用函数
    • 当组件依赖的响应式数据变化时,render 函数会重新执行,生成新的虚拟 DOM
    • 然后通过 Diff 算法更新实际 DOM
  2. watch 监听的回调函数

    • 当监听的响应式数据变化时,执行对应的回调函数
    • 例如:watch(() => state.count, (newVal) => { console.log(newVal) })
  3. computed 计算属性的 getter 函数

    • 当计算属性依赖的响应式数据变化时,重新计算并缓存结果
    • 例如:const double = computed(() => state.count * 2)
  4. 开发者手动创建的 effect 函数

    • 直接使用 effect() API 创建的自定义副作用函数
    • 例如:effect(() => { document.title = state.count })

Trigger 触发更新的机制

当响应式数据发生变化时(setdeleteProperty 操作):

  1. 调用 trigger 函数
  2. trigger 函数根据变化的属性,找到对应的 dep(依赖集合)
  3. 遍历 dep 中的所有副作用函数,依次执行
  4. 不同类型的副作用函数会通过不同的调度器(scheduler)执行

源码中的实现

@vue/reactivity 源码中:

  • effect 函数创建副作用函数并收集依赖
  • track 函数在访问响应式数据时收集依赖关系
  • trigger 函数在修改响应式数据时触发依赖更新
  • 组件的 render 函数会被包装成一个副作用函数,在组件初始化时通过 setupRenderEffect 注册

结论

组件的 render 函数确实是一种副作用函数,但副作用函数的范围更广,包括 watch 回调、computed getter 以及自定义 effect 函数等。

trigger 函数触发的是所有依赖该属性的副作用函数,而不仅仅是组件的 render 函数。这是 Vue3 响应式系统的核心设计,确保了所有依赖响应式数据的逻辑都能自动更新。