The processing method for nested response objects
Handling of Nested Reactive Objects
Vue 3's reactivity system is based on Proxy, and its approach to handling nested objects differs significantly from Vue 2. When encountering nested objects, Vue 3 recursively converts the entire object tree into reactive proxies, providing more granular dependency tracking.
Basic Handling Mechanism
When creating a reactive object with the reactive()
function, Vue 3 deeply proxies nested objects. For example:
const obj = reactive({
nested: {
count: 0
},
array: [{ value: 1 }]
})
In this example, not only is obj
itself reactive, but obj.nested
and obj.array[0]
are also automatically converted into reactive objects. This processing occurs upon first access to the property, using a lazy proxy strategy:
// Nested objects are proxied only upon first access
console.log(isReactive(obj.nested)) // true
console.log(isReactive(obj.array[0])) // true
Implementation Principle of Recursive Proxying
In baseHandlers.ts
, the Proxy's get interceptor handles nested objects as follows:
function createGetter(isReadonly = false) {
return function get(target: object, key: string | symbol) {
const res = Reflect.get(target, key)
// If the retrieved value is an object, recursively call reactive
if (typeof res === 'object' && res !== null) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
This design ensures that no matter how deep the object nesting goes, each access returns the corresponding reactive proxy.
Special Handling for Arrays
For array types, Vue 3 includes specific optimizations. When modifying array elements, reactivity is correctly triggered:
const list = reactive([{ value: 1 }, { value: 2 }])
// Modifying nested objects triggers reactivity
list[0].value = 3
// Directly setting array elements also works
list[0] = { value: 4 }
The logic for handling arrays is primarily implemented in collectionHandlers.ts
, where array mutation methods (such as push, pop, etc.) are overridden to ensure reactivity.
Avoiding Duplicate Proxying
Vue 3 uses a WeakMap to cache already proxied objects, preventing duplicate proxying:
const reactiveMap = new WeakMap()
function reactive(target) {
// If already proxied, return the cached version
const existingProxy = reactiveMap.get(target)
if (existingProxy) return existingProxy
// Create a new proxy
const proxy = new Proxy(target, baseHandlers)
reactiveMap.set(target, proxy)
return proxy
}
This caching mechanism ensures that the same original object always returns the same proxy instance.
Performance Optimization Strategies
Deep reactive conversion can incur performance overhead. Vue 3 provides two optimization methods:
- Shallow Reactivity: Use
shallowReactive
to proxy only the first-level properties
const shallow = shallowReactive({
nested: { count: 0 } // nested will not be automatically proxied
})
- Manual Non-Reactive Marking: Use
markRaw
to skip proxying
const rawObj = markRaw({ count: 0 })
const obj = reactive({ nested: rawObj }) // nested remains a raw object
Comparison with Vue 2
Vue 2 uses Object.defineProperty to implement reactivity, requiring recursive conversion of the entire object upfront:
// Vue 2 approach - recursively converts during initialization
new Vue({
data() {
return { nested: { count: 0 } } // nested is converted immediately
}
})
In contrast, Vue 3's lazy proxying strategy offers advantages in performance and memory usage, especially for large, deeply nested objects.
Practical Application Scenarios
Consider a form component handling nested data structures:
const form = reactive({
user: {
name: '',
address: {
city: '',
street: ''
}
},
preferences: {
notifications: true,
theme: 'light'
}
})
// Deep properties can be directly bound in templates
watch(() => form.user.address.city, (newVal) => {
console.log('City changed:', newVal)
})
This nested reactive handling makes complex state management more intuitive, eliminating the need to manually handle reactivity at each level.
Edge Case Handling
Certain special scenarios require attention:
- Prototype Chain Properties: Do not trigger reactivity
const parent = { count: 1 }
const child = reactive(Object.create(parent))
console.log(child.count) // 1, but modifying parent.count will not trigger reactivity
- Symbol Properties: Proxied by default
const sym = Symbol()
const obj = reactive({ [sym]: 'value' })
console.log(obj[sym]) // Reactive access
- Circular References: Automatically handled
const obj = {}
obj.self = obj
const reactiveObj = reactive(obj) // No stack overflow
Reactive Object Identity
Since each access to a nested object returns the same proxy instance, strict equality checks can be used:
const obj = reactive({ nested: {} })
console.log(obj.nested === obj.nested) // true
This feature plays a key role in dependency collection and component update optimization.
Integration with Refs
When a nested object contains a ref, Vue 3 automatically unwraps it:
const count = ref(0)
const obj = reactive({ count })
console.log(obj.count) // 0, no .value needed
obj.count++ // Direct modification
This automatic unwrapping mechanism allows refs and reactive objects to be used together without conflict.
Key Source Code Locations
The main implementation is distributed across several core files:
packages/reactivity/src/reactive.ts
- Core proxying logicpackages/reactivity/src/baseHandlers.ts
- Basic proxy handlerspackages/reactivity/src/collectionHandlers.ts
- Collection type handlers
The createReactiveObject
function is the entry point for creating reactive objects, handling caching, read-only proxies, and other logic.
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:响应式代理的创建过程
下一篇:数组的特殊响应处理