Special response handling for arrays
Special Responsive Handling of Arrays
Vue3's reactivity system is implemented based on Proxy, which is relatively straightforward for ordinary objects. However, arrays, as a special type of object, require unique handling in certain scenarios. The reactive processing of arrays needs to consider performance optimization and API compatibility while maintaining behavior patterns similar to Vue2.
Reactivity Principles of Arrays
Proxy can intercept basic array operations, but some array methods may trigger multiple updates. Vue3 optimizes performance by rewriting seven mutation methods of arrays:
const arrayInstrumentations = {}
;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {
arrayInstrumentations[method] = function(...args) {
pauseTracking()
const res = Array.prototype[method].apply(this, args)
resetTracking()
return res
}
})
This approach ensures that each array method call triggers only one dependency update, rather than triggering updates for every element change. For example, when executing arr.push(1,2,3)
, Vue3 will only collect one change instead of three.
Responsive Handling of Index Access
When directly modifying array elements via indices, Vue3's approach differs from Vue2:
const arr = reactive([1, 2, 3])
arr[0] = 10 // Triggers reactivity
This is because Proxy can intercept array index access. However, note that when setting an index beyond the current array length:
arr[100] = 100 // Does not trigger reactivity
In this case, you need to explicitly use arr.length = 101
to ensure reactivity takes effect.
Special Handling of the length Property
The length
property of arrays requires special handling because modifying length
affects existing elements:
const arr = reactive([1, 2, 3])
effect(() => {
console.log(arr.length)
})
arr.length = 1 // Triggers the effect
Vue3 internally tracks access to the length
property and triggers relevant dependencies when length
changes. When the array length is shortened, the reactive associations of deleted elements are also cleaned up.
Combining Arrays with ref
In the Composition API, arrays are often used with ref
:
const list = ref([])
list.value.push('item') // Automatically triggers updates
ref
internally wraps the array reactively, but note the access to .value
. Vue3 optimizes this, allowing direct use of ref
arrays in templates without .value
:
<template>
<div v-for="item in list" :key="item">{{ item }}</div>
</template>
Reactive Handling of Multidimensional Arrays
For multidimensional arrays, Vue3 recursively converts them into reactive objects:
const matrix = reactive([
[1, 2],
[3, 4]
])
matrix[0][1] = 5 // Triggers reactivity
Internally, Vue3 automatically performs reactive conversion when accessing subarrays:
function createReactiveArray(arr) {
return new Proxy(arr, {
get(target, key) {
const res = Reflect.get(target, key)
if (typeof res === 'object' && res !== null) {
return reactive(res)
}
return res
}
})
}
Arrays and watch
When observing arrays, note the difference between reference changes and content changes:
watch(
() => [...arr],
(newVal, oldVal) => {
// Deep comparison of array contents
},
{ deep: true }
)
For large arrays, direct observation may cause performance issues. Vue3 provides more efficient observation methods:
watch(
arr,
(newVal, oldVal) => {
// Responds to array changes
},
{ flush: 'sync' }
)
Performance Optimization Strategies
Vue3 implements several optimizations for array operations:
- Batching array changes: Multiple consecutive array operations are merged into a single update.
- Lazy reactivity: Reactive associations are only established when array elements are actually accessed.
- Caching strategy: Frequently accessed array elements are cached as reactive proxies.
For example, in virtual scrolling scenarios, Vue3 only establishes reactive associations for array elements within the visible area:
const visibleItems = computed(() => {
return largeArray.slice(startIndex, endIndex)
})
Differences Compared to Vue2
Vue3's reactive handling of arrays has several key differences from Vue2:
- Vue3 does not require the
$set
method; direct index modification triggers reactivity. - Vue3 can detect operations like
arr.length = 0
to clear an array. - Vue3 handles sparse arrays more reasonably and does not create reactivity for non-existent indices.
For example, in Vue2, you would operate on an array like this:
this.$set(this.arr, index, value)
In Vue3, you can directly do:
arr[index] = value
Edge Cases of Reactive Arrays
Certain special array operations require attention:
- Swapping element positions:
[arr[0], arr[1]] = [arr[1], arr[0]] // Triggers reactivity
- Using destructuring assignment:
const [first, ...rest] = arr // Does not establish reactive associations
- Using
Array.from
:
const newArr = Array.from(arr) // The new array is not reactive
Customizing Array Reactivity Behavior
You can implement custom reactive logic for arrays using custom ref
:
function customArrayRef(initialValue) {
const value = ref(initialValue)
return {
get value() {
return value.value
},
set value(newVal) {
if (Array.isArray(newVal)) {
value.value = Object.freeze(newVal) // Example: Freezes the array
}
}
}
}
Using Reactive Arrays in Templates
Template operations on arrays have some special behaviors:
<template>
<!-- Automatically unwraps ref arrays -->
<div v-for="item in refArray">{{ item }}</div>
<!-- Directly responds to array methods -->
<button @click="array.push(Date.now())">Add</button>
</template>
The Vue3 compiler performs special processing for array access in templates to ensure reactivity works correctly. For example, v-for="item in array.slice(0,5)"
can also maintain reactive associations.
Internal Implementation Details of Array Reactivity
Vue3 internally manages array dependencies through track
and trigger
functions:
function createArrayInstrumentations() {
const instrumentations = {}
;['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
instrumentations[key] = function(...args) {
const arr = toRaw(this)
for (let i = 0, l = this.length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '')
}
return arr[key](...args)
}
})
return instrumentations
}
This implementation ensures that search methods also correctly collect dependencies. When array elements change, related computed values based on search results will also update.
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:嵌套响应对象的处理方式
下一篇:计算属性computed的实现