Cause analysis of Vue3 using Proxy to realize data monitoring

  • 2021-12-09 07:59:26
  • OfStack

vue data bidirectional binding principle, and this method has shortcomings, and can not realize the array and object part of the listening situation; Specifically, you can also see a blog I wrote before: About the solution that Vue can't change watch arrays and objects, the latest Proxy, compared with vue2 Object. defineProperty, can achieve the effect of doubling speed and halving memory. How to implement it concretely, and why can we double the speed and halve the memory compared with the old implementation method? Let's talk about it below:

Vue initialization process

The initialization process of Vue includes Observer, Compiler and Watcher respectively. When we new and Vue, we will call Observer and traverse all attributes of data, computed or props (if it is a component) of vue object through Object. defineProperty for monitoring. At the same time, through Compiler parsing template instruction, after parsing to attributes, new 1 Watcher is bound with update function to watcher, and Observer and Compiler are associated through attributes.

Object.defineProperty

A simple example of Object. defineProperty


class Observer {
    constructor(data) {
      //  Traversal parameter  data  Property of the attribute added to the  this  Upper 
      for (let key of Object.keys(data)) {
        if (typeof data[key] === "object") {
          data[key] = new Observer(data[key]);
        }
        Object.defineProperty(this, key, {
          enumerable: true,
          configurable: true,
          get() {
            console.log(" You visited " + key);// You visited age
            return data[key];//20
          },
          set(newVal) {
            console.log(" You set up " + key); // You set up age
            console.log(" New " + key + "=" + newVal); // New age=20
            if (newVal === data[key]) {
              return;
            }
            data[key] = newVal;
          }
        });
      }
    }
  }const obj = {
    name: "app",
    age: "18",
    a: {
      b: 1,
      c: 2,
    }
  };const app = new Observer(obj);app.age = 20;console.log(app.age);app.newPropKey = " New attribute ";console.log(app.newPropKey);    // New attribute 

As you can see from the above, Object. defineProperty needs to traverse all attributes, which makes traversal much slower if the data in data/computed/props of vue object is large. Similarly, if the data in data/computed/props of vue object is large, Object. defineProperty needs to listen for all attribute changes, which takes up a lot of memory.

Proxy

Let's take a look at proxy

Proxy objects are used to define custom behaviors for basic operations (such as property lookups, assignments, enumerations, function calls, and so on)

It can be understood that a "interception" is set before the object, and when the listening object is accessed, it must pass through this layer of interception. You can process the original object in this interception and return the required data format, that is, no matter what attributes of the object are accessed, previously defined or newly added attributes will go to the interception for processing. This solves the problem that can't be monitored before.


const obj = {
  name: "krry",
  age: 24,
  others: {
    mobile: "mi10",
    watch: "mi4",
  },};const p = new Proxy(obj, {
  get(target, key, receiver) {
    console.log(" The properties to view are: " + key);  
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    console.log(" The properties set are: " + key); 
    console.log(" New attributes: " + key, " Values are: " + value); 
    Reflect.set(target, key, value, receiver);
  },});p.age = 22;console.log(p.age); p.single = "NO";console.log(p.single);p.others.shoe = "boost";console.log(p.others.shoe); The output is : The properties set are: age
 New attributes: age  Values are: 22 The properties to view are: age22

 The properties set are: single
 New attributes: single  Values are: NO The properties to view are: singleNO  The properties to view are: others
 The properties to view are: others
boost

It can be seen from the above that adding or editing attributes can be monitored without adding responsive processing again, because Proxy is an operation on objects, and as long as you access objects, you will go to the logic of Proxy. Reflect is a built-in object that provides methods to intercept JavaScript operations. These methods are the same as those of proxy handlers. Reflect is not a function object, so it cannot be constructed. The use of Proxy and Object. defineProperty seems very similar, but Proxy intercepts attributes in a higher dimension.

In Object. definePropertyVue2, for a given data, such as {count: 1}, it is necessary to intercept get and set according to the specific key, that is, count, that is:


Object.defineProperty(data, 'count', {
  get() {},
  set() {},})

It is necessary to know in advance what key is to be intercepted, which is why Vue2 can't do anything about the new attributes on the object, so it is necessary to traverse data in the initialization process of Vue to hold data changes, resulting in slower speed and larger memory.

Proxy and Proxy used by Vue3 are intercepted as follows:


new Proxy(data, {
  get(key) { },
  set(key, value) { },})

As you can see, the proxy does not need to care about the specific key, it intercepts any key on the data and reads any key on the data

Therefore, both the existing key and the new key will listen.


Related articles: