Object.defineProperty can indeed observe array elements. For example, you can iterate over an array and define getter/setter for each index:
let array = [1, 2, 3, 4, 5];
array.forEach((item, index) => {
defineReactive(array, index, item);
});
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
return val;
},
set(newVal) {
if (val === newVal) return;
val = newVal;
}
});
}
Despite this capability, Vue does not use Object.defineProperty to make direct index assignment (e.g., arr[0] = newValue) reactive. Two main reasons explain this decision:
- Unknown array length: In many real-world scenarios, the length of an array is not known in advance. Pre-defining reactive properties for all possible indices would require knowing the maximum size, which is impractical.
- Performance concerns: Arrays can be very large. Iterating over all element to define getters/setters on each index would introduce significent overhead, both during initialization and memory usage for the reactive watchers.
Instead, Vue overrides seven mutation methods on the array prototype to detect changes: push, pop, shift, unshift, splice, sort, and reverse. These intercepted methods call the original array methods and then notify the dependency system to update the view.
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
methodsToPatch.forEach(function (method) {
var original = arrayProto[method];
def(arrayMethods, method, function mutator() {
var args = [], len = arguments.length;
while (len--) args[len] = arguments[len];
var result = original.apply(this, args);
var ob = this.__ob__;
var inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2);
break;
}
if (inserted) { ob.observeArray(inserted); }
ob.dep.notify();
return result;
});
});
The observeArray function ensures that any newly added elements (objects) become reactive. This approach avoids the performence pitfalls of pre-defining reactive properties for every index while still supporting array mutations through the standard methods.