I. Problem discovery

In the previous monitoring of Object attributes, object.defineProperty was used to monitor the changes of Object attributes through setter and getter methods of objects, and then related processing was done through corresponding dependencies

For Array methods, the method operating on array. prototype does not fire the setter method for that property because there is no assignment for that property.

Therefore, when we need to monitor changes in array values, we need to rewrite the array prototype method it calls so that it can raise setter/getter methods for properties to listen on

Rewrite the array prototype method refers to modify the array prototype pointing, then rewrite the prototype method, as for the new prototype pointing to where, see the next source analysis

Second, source code analysis

The array of common methods to rewrite, mainly for the following seven methods

Pop, Shift, push, unshift, sort, splice, reverseCopy the code

In terms of how to rewrite, basically intercepting the operation on the array through the prototype chain, so as to detect the change of the operation on the array, using function hijacking to rewrite the array method

1. Listener Observer

if (Array.isArray(value)) { //1. If (hasProto) {// 2. Determine whether to support protoAugment(value, arrayMethods)} else {copyAugment(value, arrayMethods, ArrayKeys) // If there is no prototype chain will go to def method to add __ob__ attribute} this.observeArray(value) //3. } else {this.walk(value)}Copy the code
  • process

In the above part of the Observer code, the operations on arrays look something like the following

  1. Determines whether the value data (suppose an ARR array) is an array
  2. In the case of array, check whether the prototype chain is supported
  3. The protoAugment function is executed with support for prototype chains

2.protoAugment

Function protoAugment (target, SRC: Object) {target.__proto__ = SRC // Make the target's prototype chain point to SRC}Copy the code

Proto == array. prototype (arrayMethods) {arrayMethods =arrayMethods

3.arrayMethods

ArrayProto === array. protpType const arrayProto = array. prototype export const arrayMethods = Object.create(arrayProto) const methodsToPatch = [ 'push','pop','shift', 'unshift','splice','sort','reverse' ] methodsToPatch.forEach(function (method) { //1. Const original = arrayProto[method] //2. Def (arrayMethods, method, function mutator (... args) { const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': Inserted = args.slice(2) break} if (inserted) ob.observearray (inserted) // Monitor the inserted data again // observeArray is the array traversed Ob.dep.notify () // Update the notification view return result})}Copy the code

ArrayMethods functions as follows:

  1. willThe listened array ARrMethod calledthroughThe prototype chain is taken from Array.prototype. Array.prototype inherits properties from array. prototype and does not affect other properties of Array.
  2. Redefine the called method to use def functions (to listen for array changes caused by array methods)

The value of the argument passed by the call to def, with emphasis on val, the mutator function

The return value of the mutator function is the new value we need to set, that is, the value of the data we want to change.

  1. Call the array method to manipulate the array
  2. The data of the new array is determined according to the actual array method (switch) called, and its valid data is monitored again
  3. Notifies views of updates to data changes

4.def

export function def (obj: Object, key: string, val: any, enumerable? : boolean) { Object.defineProperty(obj, key, { value: val, enumerable: !! enumerable, writable: true, configurable: true }) }Copy the code

The def function is object.defineProperty ().

Summary: For Array monitoring in VUE, the call of Array method is rewritten by prototype chain. By pointing to the Array prototype method defined by myself, the new prototype has all the methods on the original Array. Prototype (without affecting the use of other attributes of the Array itself), to achieve the monitoring of Array changes

However, all of the above methods are used to detect array changes, which cannot be detected by methods like the following. Other methods are required, such as vm.$set and splice

Arr [1] = 1212// Assign arr. Length = 3; // Change the length directlyCopy the code