This is the 10th day of my participation in the More text Challenge. For more details, see more text Challenge

One, foreword

The first part mainly introduces the observation of object data changes, involving the following points:

The deep observation processing is realized when the old attribute value of the object changes to object and array.

Combined with the realization principle, the reason why the new attribute of the object cannot be observed and how to realize the data observation are explained.

In this section, the observation of array data changes (array, new object, array, normal value)


Second, array, new object, array, ordinary value observation problem

1. Problem analysis

Does adding an object, array, or normal value to an array ARR trigger an update?

let vm = new Vue({
  el: '#app'.data() {
    return { arr: [{ name: "Brave" }, 100]}}}); vm.arr.push({a:100});
vm.arr[2].a = 200;
Copy the code

Processing for array types as of the current version:

  • Overrides methods on the array chain to hijack seven prototype methods that cause changes to the original array;

  • Recursively call observe for each item in the array, making the array type realize recursive observation;

    Since observe only deals with object types, normal values in the array are not observed;

Although we have implemented the data hijacking of an array, we have not implemented the specific logic after the data hijacking:

// src/Observer/array.js

let oldArrayPrototype = Array.prototype;
export let arrayMethods = Object.create(oldArrayPrototype);

let methods = [
  'push'.'pop'.'shift'.'unshift'.'reverse'.'sort'.'splice'
]

methods.forEach(method= > {
  arrayMethods[method] = function () {
    console.log('Method ='; + method)
    // Hijack to array change, processing logic has not been implemented}});Copy the code

So, adding content to an array can trigger data hijacking, but the actual logic behind the hijacking is not implemented yet

In vue 2.x, adding an object to an array and modifying the properties of the new object trigger updates.

2. Thinking analysis

Overwrite the push method logic:

Because of the inconsistent number of incoming parameters for the seven methods, for example, push can be passed multiple parameters

3. Code implementation

When the parameter of push is an object type, you need to observe again

// src/observe/array.js

methods.forEach(method= > {
  // Current external call: arr.push
  arrayMethods[method] = function (. args) {
    console.log('Method ='; + method)
    // AOP:before native method extensions...
    // Call the array native method logic (bound to the current call context)
    oldArrayPrototype[method].call(this. args)// AOP::after native method extensions...

    // If the new attribute is a property, continue to observe
    // Splice push unshift
    let inserted = [];
    switch (method) {
      // arr.splice(0,0,100) if the splice method is used to add, there must be a third parameter, from which the content is added
      case 'splice':  // Modify delete add
        inserted = args.slice(2); // The splice method adds data from the third argument
      case 'push':    // Add forward
      case 'unshift': // Add backward
        inserted = args // Push and unshift parameters are added
        break;
    }
    // Walk through the inserted array to see if it needs to be hijacked}});Copy the code

When the parameter of push is an object type, continue to observe it.

Question 1

Array deep hijack of the observeArray method in the Observer class

Because there is no export, it is not accessible in SRC /observe/array.js’s methods.forEach

The VM is not available in the Observer class,

So add a custom attribute associated with the current this: value.ob = this;

Value: adds a custom attribute to an array or object __ob__ = this,

This: an instance of the current Observer class on which the observeArray method exists;

In SRC/observeArray, we call the observeArray method in the methods. ForEach in SRC/observeArray.

// src/observe/index.js
class Observer {
  
  constructor(value) {
    // value: adds a custom attribute to an array or object __ob__ = this,
    // this: is an instance of the current Observer class on which the observeArray method exists;
    value.__ob__ = this;

    if (isArray(value)) {
      value.__proto__ = arrayMethods;
      this.observeArray(value);
    } else {
      this.walk(value); }}}Copy the code

The __ob__ array is added and the push method is called, so you can get ob from the __ob__ attribute

// src/observe/array.js

methods.forEach(method= > {
  arrayMethods[method] = function (. args) {
    oldArrayPrototype[method].call(this. args)let inserted = null;
    let ob = this.__ob__;	// get ob from the __ob__ attribute
    switch (method) {
      case 'splice': 
        inserted = args.slice(2);
      case 'push':  
      case 'unshift':
        inserted = args 
        break;
    }
    
    // observeArray: Iterates through the inserted array and calls the observe method to continue the deep observation of the object on the new Observer
    if(inserted)ob.observeArray(inserted);// Inserted has a value, which is an array}});Copy the code

So, when you push an object or an array into an array, you continue to use the observeArray method to make the object or array responsive

Question 2

Running will result in an infinite loop

// src/observe/index.js

class Observer {

  constructor(value) {
    value.__ob__ = this;

    if (isArray(value)) {
      value.__proto__ = arrayMethods;
      this.observeArray(value);
    } else {
      this.walk(value); }}walk(data) {
    Object.keys(data).forEach(key= >{ defineReactive(data, key, data[key]); }); }}Copy the code

In the Observer class, since value.ob = this; This code

If value is an object, it goes to this.walk(value); Method to continue the loop through the object’s properties,

In this case, the attribute __ob__ is looping out, and __ob__ is an object with __ob__ on it

So, after defining the property __ob__ in the walk loop, its value is still an object, creating an infinite recursion loop

If value is an object then we go to the walk method, we loop through all the properties in the value object,

The __ob__ attribute is looping out, and ob is the current instance, which is actually an object, and will continue to be observed, resulting in an infinite loop

__ob__ cannot be traversed, otherwise it will be defineProperty and will loop endlessly.

Frozen: Properties that are frozen cannot be modified, but they can still be traversed

You need to define the __ob__ attribute with defineProperty and configure the OB attribute to be non-enumerable

// src/observe/index.js
class Observer {

  constructor(value) {
    // value.__ob__ = this; // Can be traversable enumerations, resulting in an infinite loop
    // Define the __ob__ attribute to be unenumerable, to prevent the object from continuously defining eProperty when entering the walk, which causes an infinite loop
    Object.defineProperty(value, '__ob__', {
      value:this.enumerable:false  // Cannot be enumerated
    });
    
    if (isArray(value)) {
      value.__proto__ = arrayMethods;
      this.observeArray(value);
    } else {
      this.walk(value); }}}Copy the code

After execution, the problem is solved:


Three, the end

This paper mainly introduces the observation of array data changes:

  • Implement the concrete logic that has overridden the prototype method after the array data changes have been hijacked;
  • Analysis of the observation situation when the array data changes;

At this point, the data hijacking is complete

Next, the process of data rendering