preface

Vue3 took two years to develop, 99 contributors, 2,600 submissions, and 628 PR

Vue3 supports most of the features of 2.

Performance improvements:

1. Reduce package size by 41%

2. Initial render is 55% faster and updates are 133% faster

3. Reduced memory usage by 54%

New Composition API and other features, and better typescript support.

Learning and using Vue3 is imperative.

In this article, we will briefly compare the responsivity principle of Vue2 and Vue3.

The responsive principle of Vue2.0

1. The first Object. DefineProperty

Vue2 uses the native JS API Object. DefineProperty to intercept access to the name attribute of a data Object.

When accessing, the get function is executed. When a property changes, listen for that change, using the set function.

Here’s an example:

const data = {};
let name = 'Vue';
Object.defineProperty(data, 'name', {get: function(){
      console.log('get');
      return name;
    },
    set: function (newValue){
      console.log('set');
      name = newValue;
      // Re-render the view}})Copy the code

2. Basic responsive implementation

I have many more, reactive procedures, which look like this:

const data = {
    name: 'OrzR3'.age: 30
}
// become responsive data
observer(data);
​
function observer(target){
    if(typeoftarget ! = ='object' || target === null) {return target;
    }
    for(let key in target){
        defineReactive(target, key, target[key])
    }
}
​
function defineReactive(target, key, value){
    Object.defineProperty(target, key,{
      get(){
          return value;
      },
      set(newValue){
          if(newValue ! == value){ value = newValue();console.log('Update view');
          }
      }
    })
}
​
data.name = 'Test';
// Console prints to update the view
Copy the code

Vue source code is a bit more complex, judging more cases, but the core logic, that’s the logic.

3. Handle complex objects

If the property in the object is still an object, go ahead and call observe, in the set method, listen for the newly set value

 const data = {
    name: 'OrzR3'.age: 30.friend: {friendName: 'sven'}}// become responsive data
  observer(data);
  
  function observer(target){
    if(typeoftarget ! = ='object' || target === null) {return target;
    }
    for(let key in target){
      defineReactive(target, key, target[key])
    }
  }
​
  function defineReactive(target, key, value){
    // If the property of the object is still an object, continue to call observe
    observer(value);
    Object.defineProperty(target, key,{
      get(){
        return value;
      },
      set(newValue){
        // Listen for the new value
        observer(newValue);
        if(newValue ! == value){ value = newValue();console.log('Update view');
        }
      }
    })
  }
​
  data.name = 'Test';
  data.age = { number: 40 }
  // Console prints to update the view
Copy the code

4. Handle arrays

The view is updated as it changes based on the index.

With methods like push, you manipulate arrays without updating the view.

The method that needs to override the original array.

const oldArrayProto = Array.prototype;
const newArrProto = Object.create(oldArrayProto);
console.log('old', oldArrayProto);
console.log('new', newArrProto);
['push'.'pop'.'shift'.'unshift'.'splice'].forEach(methodName= >{
    newArrProto[methodName] = function(){
        console.log('Update view');
        oldArrayProto[methodName].call(this. arguments); }})Copy the code

const data = {
    name: 'OrzR3'.age: 30.friend: {friendName: 'sven'
    },
    colors: ['red'.'orange'.'green']}// Save the array prototype
const oldArrayProto = Array.prototype;
const newArrProto = Object.create(oldArrayProto);
console.log('old', oldArrayProto);
console.log('new', newArrProto);
['push'.'pop'.'shift'.'unshift'.'splice'].forEach(methodName= >{
newArrProto[methodName] = function(){
  console.log('Update view');
  oldArrayProto[methodName].call(this. arguments); }})// become responsive data
observer(data);
  
function observer(target){
    if(typeoftarget ! = ='object' || target === null) {return target;
    }
    // Determine when to convert data into responsive data
    // If the data is an array, modify the prototype to the newly created prototype
    if(Array.isArray(target)){
      target.__proto__ = newArrProto;
    }
    for(let key in target){
      defineReactive(target, key, target[key])
    }
}
​
function defineReactive(target, key, value){
    // If the property of the object is still an object, continue to call observe
    observer(value);
    Object.defineProperty(target, key,{
      get(){
        return value;
      },
      set(newValue){
        // Listen for the new value
        observer(newValue);
        if(newValue ! == value){ value = newValue();console.log('Update view');
        }
      }
    })
}
​
data.name = 'Test';
data.age = { number: 40 }
// The view is updated as it changes according to the index
data.colors[0] = 'blue';
// With methods like push, arrays are manipulated without updating the view
// Need to override the original array method
data.colors.push('blue');
// Console prints to update the view
Copy the code

disadvantages

  • Object. DefineProperty deep listening, poor performance.

  • Problems with responsive data using Object.defineProperty:

  • If the data is an object and the hierarchy is deep, the depth listening is continued until the property is a common value. When the data is complex, it freezes.

Therefore, use proxy in VUe3 to solve this problem. Listening is enabled only when the proxy uses data.

  • Also, using Object.defineProperty for listening, when data is deleted, new properties are added to the data and the view is not updated.

  • Object.defineproperty has no way to handle data deletion and new attributes.

  • Therefore, when data is deleted, the vue.delete method is used. The vue. set method is used to add attributes to data.

This series of problems has been optimized in Vue3.0.

The principle of Vue3.0 responsiveness

Vue3.0 uses proxy instead of defineProperty in VUe2.0 to monitor mechanism changes and achieve better performance. Makes up for some of the shortcomings of Vue2.0.

For example, use a proxy to implement responsiveness

 // proxy The proxy is stored in WeakMap
 // toProxy stores the proxy object
  const toProxy = new WeakMap(a);// toProxy stores the object before the proxy
  const toRaw = new WeakMap(a);function trigger() {
    console.log("Trigger view update");
  }
​
  function isObject(target) {
    return typeof target === "object"&& target ! = =null;
  }
​
  function reactive(target) {
    if(! isObject(target)) {return target;
    }
    // If the proxy table already exists, return the result
    let proxy = toProxy.get(target);
    if (proxy) {
      return proxy;
    }
    // If the object has already been propped, return it unchanged
    if (toRaw.get(target)) {
      return target;
    }
    const handlers = {
      set(target, key, value, receiver) {
        View updates can be triggered directly if private attributes are triggered
        if (target.hasOwnProperty(key)) {
          trigger();
        }
        return Reflect.set(target, key, value, receiver);
      },
      get(target, key, receiver) {
        const res = Reflect.get(target, key, receiver);
        if (isObject(target[key])) {
          // If the property is an object, call recursively
          return reactive(res);
        }
        return res;
      },
      deleteProperty(target, key) {
        return Reflect.deleteProperty(target, key); }};// proxy es6
    let observed = new Proxy(target, handlers);
    toProxy.set(target, observed); // The original object, the result of the proxy
    toRaw.set(observed, target);
    return observed;
  }
​
  let obj = {
    name: "OrzR3".list: [1.2.3]};let p = reactive(obj);
  p.name = "sven";
  p.list.push(4);
Copy the code

The appendix

WeakMap

WeakMap is an ES6 syntax that represents weakly referenced objects.

In JavaScript, when we create an object, we create a strong reference:

var obj = new Object(); It is only possible to reclaim objects referenced by obj if we manually set obj = null.

And if we can create a weakly referenced object:

Var obj = new WeakObject(); We do nothing but wait quietly for the garbage collection mechanism to execute, and objects referenced by OBj will be collected.

WeakMap saves you the need to manually delete an object’s associated data, so consider using WeakMap when you can’t or don’t want to control the life cycle of the associated data.

Reflect and Proxy

Proxy and Reflect are apis introduced by ES6 for manipulating objects.

The Proxy intercepts operations such as reading and function invocation of the target object, and then processes the operations. It does not operate directly on objects, but rather acts like a proxy mode, operating through objects’ proxy objects, and as it does so, additional operations can be added as needed.

Reflect can be used to get the behavior of a target Object, which is similar to Object but more readable and provides a more elegant way to manipulate objects. Its methods correspond to Proxy.

See documentation for details:

www.runoob.com/w3cnote/es6…