This part is the soul of the entire Vue3, more important than VDOM.
🍀 1. The core idea of Vue3 responsiveness (Proxy-based)
Vue2 uses Object.defineProperty, which has flaws:
- Unable to monitor array subscripts
- Unable to monitor new properties of object
- Deep object dependencies are difficult to track
- Performance is affected by large number of getters/setters
Vue3 was changed to Proxy to solve all the above pain points and support:
- ✔ Deep monitoring
- ✔ Array/Map/Set native monitoring
- ✔ Lazy access
- ✔ WeakMap caching to improve performance
Core structure:
state → Proxy(wrapper) → get → track → effect → trigger → update🔥 2. Important components of responsiveness (the three core components)
The Vue3 reactivity package contains three cores:
① effect()
Function: Register side effect functions (such as component render)
The essence is:
Register the currently executed function as a "dependency" and re-execute it when the data changes.
② track()
Function: dependency collection Trigger scene: getter
When you visit:
state.countVue3 will call:
track(target, key)Bind the currently active effect to this key.
③ trigger()
Function: Distribute updates Trigger scene: setter
When executing:
state.count++Vue will call:
trigger(target, key)Triggers all effects that depend on this key to re-execute (update the view).
🧠 3. Vue3 responsive data structure (very important)
The dependency collection relationship is as follows:
targetMap = WeakMap()
↓
target (state 对象)
↓
depsMap = Map()
↓
key → Dep(Set of effect)It can be drawn like this:
WeakMap
└── target1 (reactive 对象)
└── Map
├── key1 → Set(effectA, effectB)
├── key2 → Set(effectC)
└── target2
└── ...Explanation:
- Each reactive object is a
target - Each key corresponds to a dependency set
Dep - Dep is a Set containing multiple effects.
🎯 4. Implementation mechanism of reactive()
Vue3's reactive() internally creates a Proxy:
function reactive(target) {
return new Proxy(target, mutableHandlers)
}The most critical one is get/set:
get → track
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
track(target, key)
return res
}set → trigger
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
trigger(target, key)
}
return result
}There are only two things in the Proxy's handler:
get → track set → trigger
🚀 5. Effect core logic (relies on the collected brain)
effect(fn) will:
- Save fn to the global variable activeEffect
- Execute fn()
- Collect activeEffect when tracking
Source code pseudocode:
let activeEffect = null
export function effect(fn) {
const effectFn = () => {
activeEffect = effectFn
fn()
activeEffect = null
}
effectFn()
return effectFn
}⚡ 6. track implementation (dependency collection)
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
dep.add(activeEffect)
}Summary: Access data → Add the current effect to the dependency table
💥 7. Trigger implementation (dispatching updates)
When data changes:
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effectFn => effectFn())
}
}The essence is:
All effects bound by key are re-executed (re-rendered)
🧨 8. How to avoid infinite loops in dependency tracking?
Vue 3 uses "scheduler" to avoid effect recursive triggering:
effect(fn, {
scheduler(job) {
queueJob(job)
}
})Generally, the effects rendered by components will go through the scheduler to update them in batches and avoid repeated rendering.
📌 9. The essence of ref()
ref() is also a wrapper of reactive:
export function ref(value) {
const wrapper = {
value
}
return reactive(wrapper)
}That is to say:
const count = ref(0)Equivalent to:
reactive({ value: 0 })access count.value → get → track Modify count.value → set → trigger
📦 10. The internal mechanism of computed (very clever)
computed uses lazy effect + cache:
Pseudo code:
function computed(getter) {
let dirty = true
let value
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true
}
})
return {
get value() {
if (dirty) {
value = effectFn()
dirty = false
}
return value
}
}
}computed features:
- getter internal dependency change → dirty = true
- The value will be recalculated next time it is accessed.
- No calculation without access (lazy)
Very efficient.
🏆 11. Responsive complete process (super clear)
First render:
- effect(render)
- Access state.xxx → track in render
- Establish dependencies
Data changes:
- state.xxx = newValue → trigger
- Find all effects
- scheduler → queueJob
- Re-render (effect execution)
- patch → update DOM
Flowchart:
get → track → 收集依赖
set → trigger → effect → patch → DOM 更新🔥 12. A mini-reactive implementation that you can run directly (core line 30)
let activeEffect = null
const targetMap = new WeakMap()
function effect(fn) {
const effectFn = () => {
activeEffect = effectFn
fn()
activeEffect = null
}
effectFn()
}
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) targetMap.set(target, (depsMap = new Map()))
let dep = depsMap.get(key)
if (!dep) depsMap.set(key, (dep = new Set()))
dep.add(activeEffect)
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) dep.forEach(fn => fn())
}
function reactive(target) {
return new Proxy(target, {
get(t, k, r) {
const res = Reflect.get(t, k, r)
track(t, k)
return res
},
set(t, k, v, r) {
const old = t[k]
const result = Reflect.set(t, k, v, r)
if (old !== v) trigger(t, k)
return result
}
})
}You can use it:
const state = reactive({ count: 0 })
effect(() => {
console.log("count changed:", state.count)
})
state.count++🎓 13. At this point you have mastered:
- ✔ reactive principle
- ✔ effect / track / trigger
- ✔ WeakMap → Map → Set data structure
- ✔ ref/computed internal mechanism
- ✔ scheduler scheduling system
- ✔ Proxy interception logic
It has reached the level where you can view the Vue3 source code.