Implementation Principle and Cleanup Mechanism of Nested Effects in Vue3's Reactivity System

Implementation Principle and Cleanup Mechanism of Nested Effects in Vue3's Reactivity System

1. Problem Description

In Vue3's reactivity system, an effect might execute in a nested manner (e.g., component rendering triggers another effect). If nested dependencies are not handled properly, it can lead to confusion in dependency collection (such as a parent effect depending on properties of a child effect). Vue3 addresses this issue through an effect stack and an activeEffect switching mechanism.


2. Core Concepts: Effect Stack and Active Effect

  • Effect Stack (effectStack): Used to store effect functions that are currently being executed, pushed/popped in execution order.
  • ActiveEffect: The currently active effect being processed; dependencies are associated with this variable during collection.

3. Execution Flow of Nested Effects

Assume the following code:

effect(() => {  
  console.log("Parent effect executed");  
  effect(() => {  
    console.log("Child effect executed");  
  });  
});  

Step 1: Initial Execution

  • The outer effect is pushed onto the effect stack, and activeEffect points to the parent effect.
  • The function body of the parent effect begins execution.

Step 2: Inner Effect Execution

  • Upon encountering the inner effect, the execution of the parent effect is first paused. The inner effect is pushed onto the stack, and activeEffect switches to the child effect.
  • The child effect's function body is executed, and its dependencies are collected.

Step 3: Inner Execution Completion

  • The child effect finishes execution, is popped from the stack, and activeEffect is restored to the parent effect.
  • The remaining code of the parent effect continues to execute.

Key Point:

The stack structure ensures that activeEffect always points to the effect currently being executed, preventing confusion in dependency collection.


4. Dependency Cleanup Mechanism (Avoiding Stale Dependencies)

When an effect re-executes, old dependencies must be cleaned up before new ones are collected. Vue3 achieves this through a deps array for reverse cleanup.

Step 1: Dependency Recording

Each effect instance maintains a deps array, storing all associated dependency sets (i.e., Set objects corresponding to reactive properties).

Step 2: Cleaning Up Old Dependencies

Before an effect re-executes, it iterates through deps, removing the effect from each dependency set to avoid leaving behind stale dependencies.

Step 3: Re-collection

When the effect function executes, dependencies are re-added to the latest dependency sets.


5. Source-Level Example (Simplified Version)

let activeEffect;  
const effectStack = [];  

class ReactiveEffect {  
  constructor(fn) {  
    this.fn = fn;  
    this.deps = []; // Stores dependency sets  
  }  
  run() {  
    // Clean up old dependencies  
    cleanupEffect(this);  
    // Push onto stack and switch active effect  
    effectStack.push(this);  
    activeEffect = this;  
    // Execute function (triggers dependency collection)  
    this.fn();  
    // Pop from stack and restore previous effect  
    effectStack.pop();  
    activeEffect = effectStack[effectStack.length - 1];  
  }  
}  

function cleanupEffect(effect) {  
  for (const dep of effect.deps) {  
    dep.delete(effect); // Remove the effect from the dependency set  
  }  
  effect.deps.length = 0; // Clear the deps array  
}  

6. Practical Scenario: Nested Component Rendering

  • Parent component rendering triggers the parent effect, while child component rendering triggers the child effect.
  • The effect stack ensures that child component dependencies are correctly associated with the child effect, not the parent effect.

7. Summary

  • Nested Effects: Managed via a stack structure to ensure accurate dependency collection.
  • Dependency Cleanup: Achieved through reverse association via deps, avoiding stale dependencies.
  • Performance Optimization: Prevents unnecessary dependency associations, enhancing the efficiency of the reactivity system.