Skip to content
Created: February 20, 2023

Vue3's responsive special processing mechanism for arrays, maps, and sets, this is the most complex and valuable part of the reactivity system, and it is also a knowledge point that only experienced developers will encounter.

🧠 1. Why do arrays, Maps, and Sets need extra special treatment? ​

Because of these 3 types of data structures:

  • There are non-ordinary attribute access (length, size)
  • There are method operations (push, delete, set...)
  • There are iterator dependencies (for...of, keys, entries)
  • There is implicit reading of itself (accessing length will trigger track)
  • There is a complex key → value relationship (Map/Set is not an enumerable key like ordinary objects)

These cannot be handled by simple "get/set".

Vue3 specially wrote handlers for this:

mutableHandlers        // 对象
mutableCollectionHandlers   // Map/Set 专用
arrayInstrumentations       // 数组方法适配

👇Let’s talk about it one by one.

🧩 2. Reactive special processing of arrays ​

The key points of Vue3 for arrays are:

2.1 Separate dependency tracking of array subscript + length ​

For example:

js
state.arr.length // 依赖 length
state.arr[1]     // 依赖 index=1

When executing:

js
state.arr.push(123)

Internally it affects:

  • length
  • new index (such as arr[3])

Vue3 will:

push/pop/shift/unshift/splice will trigger: ​

  • trigger(target, 'length')
  • trigger(target, index)

Because once length is changed, any effect that depends on array.length must be re-executed.

2.2 Prevent recursive triggering in push ​

The Vue3 source code wraps the array method with a layer (arrayInstrumentations) to prevent this situation:

js
const arr = reactive([])
effect(() => {
  arr.push(1)   // 会触发 set → trigger → effect 循环
})

Vue will temporarily "turn off" dependency collection internally, and will not track when executing these array methods.

🧩 3. Responsive special processing of Map and Set ​

Vue3's Map/Set responsive processing is the most essential and complex part of reactivity!

Core difficulties:

OperationsFeatures
Map.setNon-covering (new key) vs covering (old key)
Map.deletewill change size
Map.forEach / for...ofIteration dependency needs to monitor all element changes
Map.getDepends on key alone
Set.addNew elements vs existing elements

Vue3 specifically implements Map/Set:

mutableCollectionHandlers
  get
  set
  add
  delete
  clear
  iterator methods

🧠 4. Key data structures of Map and Set (super important) ​

Vue’s dependency collection core has two more types:

ITERATE_KEY           // for...of、keys()、values() 的依赖
MAP_KEY_ITERATE_KEY   // 特别为 Map 的 key 迭代实现

Example:

js
effect(() => {
  for (const key of map.keys()) {
    console.log(key)
  }
})

Vue will track:

track(map, ITERATE_KEY)

This way: When map adds a new key or deletes a key → trigger ITERATE_KEY → re-execute the iteration effect.

🚀 5. Map.set’s responsive logic (very critical) ​

Source code logic (simplified version):

ts
set(key, value) {
  const hadKey = target.has(key)
  const oldValue = target.get(key)

  target.set(key, value)

  if (!hadKey) {
    trigger(target, 'add', key)
  } else if (oldValue !== value) {
    trigger(target, 'set', key)
  }
}

Meaning: ​

  • New key: triggers "add" → affects ITERATE_KEY (iterative dependency)
  • Old key override: trigger "set" → affects key's dependency

Example:

Case 1: Overwriting key ​

js
map.set('a', 1)
effect(() => {
  console.log(map.get('a'))
})

map.set('a', 2)   // 触发 get('a') 的 effect

Case 2: New key triggers iterative update ​

js
effect(() => {
  for (const [k, v] of map) { }
})

map.set('b', 123)
// 会触发迭代 effect,而不是 get('a')

🔥 6. Special logic of Map.delete and clear ​

ts
delete(key) {
  const hadKey = target.has(key)
  const result = target.delete(key)

  if (hadKey) {
    trigger(target, 'delete', key)
  }
}

delete will trigger:

  • Dependency pointing to key
  • Iterative dependencies (because key is reduced)

clear() triggers all dependencies:

ts
trigger(target, 'clear')

🧩 7. Special logic of Set.add ​

ts
add(value) {
  const had = target.has(value)
  target.add(value)

  if (!had) {
    trigger(target, 'add', value)
  }
}

Note:

Set.add will not trigger an update if added repeatedly (because there are no changes), Vue will determine this internally.

🌈 8. Special treatment for iterator methods (for…of, keys, values, entries) ​

Vue3 has a proxy for each iterator method, such as:

ts
for (const item of reactiveMap) { }

for (const [key, val] of reactiveMap.entries()) { }

reactiveMap.forEach((val, key) => { })

These are not ordinary get/set and require track:

Map’s iteration dependencies: ​

ts
track(target, ITERATE_KEY)

keys() additional uses: ​

ts
track(target, MAP_KEY_ITERATE_KEY)

This can distinguish:

  • Changing key → affects keys()
  • Changing value → affects entries() / values()

This is a very subtle aspect of Vue3's design.

🌟 9. Summary: dependency types triggered by different operations ​

data structureoperationtracktrigger
Arraygetindex / length
push/poptemporarily disable tracklength + index
Mapgetkey
setkey / ITERATE_KEYkey or iteration dependency
deletekey + ITERATE_KEY
for…ofITERATE_KEYadd/delete
keys()MAP_KEY_ITERATE_KEY
SetaddITERATE_KEYadd
deleteITERATE_KEYdelete
for…ofITERATE_KEYadd/delete

🧨 10. Why can Vue3 accurately determine “which part needs to be updated”? ​

Relying on a two-layer mechanism:

① track has different types of dependencies ​

  • key dependency
  • length depends on
  • iterative dependency
  • keys iteration dependency

② trigger precise scheduling corresponding effect ​

For example:

js
map.set(key, newVal)

Only triggers:

  • dep of key
  • iterator dep (if new)

Irrelevant effects will not be triggered randomly.

This is the key to why Vue3 performs far better than Vue2.