This is the fifth day of my participation in Gwen Challenge

One, foreword

In the first part, we mainly introduced how to realize the deep hijacking of object attributes in the process of Vue data initialization

The core idea is recursion, the main process is as follows;

Data = isFunction(data)? data.call(vm) : data; Data = observe(data); data = observe(data); data = observe(data); Walk through property 5. DefineReactive (Object.defineProperty) for each attribute implements single-layer data hijacking of Object attributes 6. In defineReactive, if the attribute value is an object type, observe the current object attribute by calling Observe (that is, recursive steps 3 to 5), thus implementing deep data hijacking of the object attribute

This article continues to introduce the hijacking of array types in the Vue data initialization process


Second, object hijacking review

1, the Demo

Data Deep observation of object properties in data, that is, the case where object properties are objects (including multiple layers)

let vm = new Vue({
  el: '#app'.data() {
    return { message: 'Hello Vue'.obj: { key: "val" }, a: { a: { a: {}}}}});Copy the code

How does Vue handle when the properties in Data are arrays


Three, array type processing

1. Current logic analysis

According to the current version of processing logic, all object types are deeply observed, including arrays

let vm = new Vue({
  el: '#app'.data() {
    return { message: 'Hello Vue'.obj: { key: "val" }, arr: [1.2.3]}}});Copy the code

As you can see, each item in the array has been added to get and set methods, which is equivalent to realizing the deep observation of the array

Note: Object.defineProperty supports hijacking of array data types

2. Trade-offs between Vue and performance

In vue 2.x, data hijacking by modifying array indexes and lengths is not supported;

So why did Vue choose not to support the observation of array indexes when it could have been implemented?

For example, if the amount of data in an array is very large:

let vm = new Vue({
  el: '#app'.data() {
    return { arr:new Array(9999)}}});Copy the code

At this point, the array 9999 data, will be added to all get, set methods

This is a bit trickier: in order to do array index hijacking, you need to process each item in the array

Also, arrays can hijack index updates via defineProperty, though

But is it really necessary in a real development scenario? The operation arr[888] = x seems to be rarely used

Therefore, to balance performance against requirements, the Vue source code does not use defineProperty to handle arrays

This, of course, makes it impossible to trigger a view update in Vue by directly changing the index or length

3, array hijacking ideas

The core goal is to implement array responsiveness:

Vue says that these 7 methods can change the original array: push, POP, splice, Shift, unshift, reverse, and sort. This approach also directly causes vue2 to change the array index and length without triggering view updatesCopy the code

Implementation of deep hijacking of object attributes:

  1. SRC /observe/index. Js# observe
  2. New Observer if the data is of object type
  3. When the Observer is initialized, it iterates through Object attributes, recursing object.defineProperty one by one

Arrays are also objects, so separate out the processing logic for arrays. That is to rewrite 7 variation methods

// src/utils

/** * check if it is an array *@param {*} val 
 * @returns * /
export function isArray(val) {
  return Array.isArray(val)
}

// src/observe/index.js
import { arrayMethods } from "./array";

class Observer {
  constructor(value) {
    if(isArray(value)){
      // Handle array types separately: override 7 variation methods
    }else{
      this.walk(value); }}}Copy the code

4, array method interception ideas

  • Rewriting method needs to realize hijacking operation of data changes on the basis of native method
  • Only arrays in reactive data are overridden. Non-reactive arrays are not affected

Therefore, the 7 methods of the array in the reactive data are intercepted, that is, the overriding method is preferred, and the other methods also follow the native logic

Array method search, first look up their own method (that is, rewrite method), can not find the chain to check (native method)

5, the implementation of array method rewrite

// src/Observer/array.js

// Get the prototype method of the array
let oldArrayPrototype = Array.prototype;
Arraymethods.__proto__ == oldArrayPrototype
export let arrayMethods = Object.create(oldArrayPrototype);

// Overwrite seven methods that can cause changes to the original array
let methods = [
  'push'.'pop'.'shift'.'unshift'.'reverse'.'sort'.'splice'
]

// Execute method overwrites on the array itself, intercepting methods with the same name on the chain
methods.forEach(method= > {
  arrayMethods[method] = function () {
    console.log(Method = 'array method =' + method)
  }
});
Copy the code

Add a new Observer to the array method override logic:

// src/observe/index.js
import { arrayMethods } from "./array";

class Observer {
  constructor(value) {
    // The value is an array and the value is an object
    if(isArray(value)){
      value.__proto__ = arrayMethods; // Change the array's prototype method
    }else{
      this.walk(value); }}}Copy the code

Test array method rewrite:

Array chains:

  • Array. proto: Contains seven override methods
  • Array.proto. proto: Primitive method

6, the implementation of array method interception

// src/state.js#initData

function initData(vm) {
    let data = vm.$options.data;
    data = isFunction(data) ? data.call(vm) : data;
    observe(data);	// The array prototype method has been rewritten after the observe new Observer method

    // Test the intercepting effect of array methods
    data.arr.push(Awesome!); 
    data.arr.pop()
}
Copy the code

  • Arraymethods. push: will find overridden push methods in the array itself, will not continue to look up the chain, implementation of interception
  • Arraymethods. pop: No overriding method found on array itself, continue to find native pop method on chain

Four, the end

This paper mainly introduces the Vue data initialization process, the array type of data hijacking, the core has the following points:

For performance reasons, Vue does not recursively hijack data of array type using Object.defineProperty, but rather intercepts and overwrites seven methods that can cause changes to the original array

Next, data broker implementation