Vue 3'sEffect (side effects)It is the core mechanism of the entire responsive system, responsible for managing dependency tracking and responsive triggering. Understanding its role and principles is crucial to mastering the underlying mechanisms of Vue.
1. Core role
1. Dependency Tracking
- Automatically track the use of responsive data in side effect functions.
Sample code:
import { reactive, effect } from 'vue' const obj = reactive({ count: 0 }) effect(() => { (`count is: ${}`) })
- When executed for the first time
effect
When, function() => (...)
Will be run. - Trigger
of
get
Operation, trigger dependency collection (replace the currenteffect
Related to)。
2. Automatic Re-run
When the dependency of responsive data changes, the side effect function is automatically re-executed:
++ // Trigger dependency update,Console Print "count is: 1"
3. Support advanced APIs
-
computed
、watch
The underlying layers of component rendering functions, etc. all depend oneffect
accomplish.
II. Implementation principle
1. Core category: ReactiveEffect
Vue 3ReactiveEffect
The logic of class encapsulation side effects, the simplified source code structure is as follows:
class ReactiveEffect<T = any> { // All dependencies of the current effect (other responsive objects) deps: Dep[] = [] // Constructor parameters constructor( public fn: () => T, // Side effect function public scheduler?: () => void // Scheduling function (control the re-execution method) ) {} // Run side effects (trigger dependency collection) run() { activeEffect = this // Mark the currently running effect try { return () } finally { activeEffect = undefined } } // Stop listening stop() { /* Remove itself from all dependencies */ } }
2. Dependence collection process (Track)
Data structure:
type Dep = Set<ReactiveEffect> // Dependency collectiontype TargetMap = WeakMap<Object, Map<string, Dep>> // Globally dependent storage
-
Trigger timing: Responsive data
get
When the operation is triggered. -
process:
According to the responsive object (
target
) and key (key
) Find deposittargetMap
Dependency set (dep
)。Will currently active
activeEffect
Add todep
middle.At the same time,
dep
join in(Reverse record, used for cleanup).
3. Trigger update (Trigger)
-
Trigger timing: Responsive data
set
When operating. -
process:
according to
target
andkey
fromtargetMap
Get the correspondingdep
gather.Traversal
dep
All ineffect
:- If there is
scheduler
(likecomputed
), execute scheduler (optimize performance). - Otherwise, execute directly
()
。
- If there is
4. Scheduler
Allow controleffect
How to re-execute:
effect(() => { () }, { scheduler(effect) { // If the effect is pushed into the microtask queue, it will be executed asynchronously queueMicrotask() } })
-
Application scenarios:
-
watch
Asynchronous batch update. -
computed
Lazy update of the value of .
-
3. Key optimization design
1. Nested Effect stack
Use stack structureeffectStack
Track nested effects:
function run() { if (!(this)) { try { ((activeEffect = this)) return () } finally { () activeEffect = effectStack[ - 1] } } }
- Solve the problem: The dependency confusion when component nesting.
2. Cleanup mechanism
Clean up old dependencies before each effect execution:
function run() { cleanup(this) // Clean up old dependencies collected before // ...and regain new dependencies}
-
Solve the problem: Invalid dependencies caused by dynamic branch logic (such as
v-if
Conditional dependency caused by switching).
3. Lazy execution
Configurable not to execute effect immediately:
const runner = effect(fn, { lazy: true }) runner() // Manual execution
-
Application scenarios:
computed
Delay calculation when attribute initialization.
4. Relationship with Vue components
1. Component Rendering
Componentsrender
The function is wrapped ineffect
middle:
function setupRenderEffect(instance) { effect(() => { const subTree = () patch(, subTree) = subTree }, { scheduler: queueJob }) // Asynchronously update the queue}
2. Computed implementation
computed
passeffect
+ Scheduler implements lazy update:
const computedRef = new ComputedRefImpl( getter, () => { // Scheduler if (!this._dirty) { this._dirty = true trigger(this, 'set', 'value') } } )
3. Watch API
watch
based oneffect
The scheduler implements asynchronous callbacks:
function watch(source, cb, { flush } = {}) { let scheduler if (flush === 'sync') { scheduler = cb } else { // 'post' or other default scheduler = () => queuePostFlushCb(cb) } effect(() => traverse(source), { scheduler }) }
5. Comparison with Vue 2
characteristic | Vue 2 (Watcher) | Vue 3 (Effect) |
---|---|---|
Dependency tracking | Triggered by traversing datagetter
|
Automatic tracking with Proxy/Reflect |
Update granularity | Relying on component-level checking | Targeted update based on precise dependency |
Performance optimization | Need to be handwrittenpureComputed wait |
Built-in automatic dependency cleaning and scheduling mechanism |
Memory management | Prone to memory leaks (old Dep reference issues) | Automatically release useless dependencies through WeakMap |
6. Source code flow diagram explanation
+---------------------+ | Reactive Object | +----------+----------+ │ When accessing properties ▼ +---------------------+ | trigger get acting +----→ track(target, key) +---------------------+ │ ▲ ▼ Storage dependencies │ +---------------------+ +----------+ targetMap | | (WeakMapstructure) | +---------+-----------+ │ ▼ +---------------------+ | depsMap (Map) | | (key → Dep Set) | +---------+-----------+ │ ▼ +---------------------+ | dep (Set) | | (Store all associated effect)| +---------------------+
Summarize
Vue 3'seffect
Become the core of a responsive system through the following mechanisms:
- Proxy dependency collection: Accurately track the use of responsive data.
- Scheduler control: Provides flexible callback execution methods.
-
Memory security:pass
WeakMap
Automatically manage dependencies. - Framework-level optimization: Supports core functions such as component rendering, computing properties, and watch.
This is the end of this article about the role and principles of vue 3 effect. For more related content of vue 3 effect, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!