Vue2. X response analysis:

Core API: Object. DefineProperty

The responsive principle: Views update as data changes

Simple analysis:

  1. Loop through all the properties of the data Object and convert them to getters/setters using Object.defineProperty
  2. Object: Recursive traversal
  3. Array: Function interceptor that overrides array prototype methods

Disadvantages:

  • Deep monitoring, need to recurse to the end, one-time calculation is large
  • Unable to native listen array, requires special handling
  • Vue. Set and Vue. Delete are used to handle the failure of listening on new attributes and deleting attributes

Code simple demo:

// Publish subscribe mode
class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  // Notify the view to update
  notify() {
    this.subs.forEach(item= >{ item.update(); }); }}/ / observer
class Watch {
  constructor() {
    Dep.target = this;
  }
  update() {
    console.log("Update view"); }}function render() {
  console.log("Update view");
}

function defineReactive(obj, key, val) {
  const dep = new Dep();
  observal(val);
  Object.defineProperty(obj, key, {
    get: function() {
      console.log("get", val);
      dep.addSub(Dep.target);
      return val;
    },
    set: function(newVal) {
      if(newVal ! = val) {console.log("set", val);
        // The new value also requires deep listeningobserval(val); val = newVal; dep.notify(); }}}); }// Rewrite the prototype
const nativeArrayProperty = Array.prototype;
// Create a new object without affecting the prototype
const arrayPro = Object.create(nativeArrayProperty);
const methodsToPatch = [
  "push"."pop"."shift"."unshift"."splice"."sort"."reverse"
];
methodsToPatch.forEach(method= > {
  arrayPro[method] = function() {
    nativeArrayProperty[method].call(this. arguments);// Update the view
    render();
  };
});
// Listen for object properties
function observal(target) {
  if (typeoftarget ! ="object" || target == null) {
    return target;
  }
  if (Array.isArray(target)) {
    target.__proto__ = arrayPro;
  }
  for (let key intarget) { defineReactive(target, key, target[key]); }}class TVue {
  constructor(options) {
    this.data = options.data;
    observal(this.data);
    newWatch(); }}let vue = new TVue({
  data: {
    leftNode: 1.rightNode: "20".info: {
      // Deep monitor
      name: "luck".age: 10
    },
    nums: [10.20.30]}});console.log(vue.data.leftNode);
vue.data.info.age = 20
vue.data.nums.push(40);

Copy the code

The principle of Vue3.0 responsiveness

Vue3 uses the ES6 Proxy feature to achieve responsiveness

Proxy

Proxy can be understood as a layer of “interception” before the target object. All external access to the object must pass this layer of interception. Therefore, Proxy provides a mechanism for filtering and rewriting external access. The word Proxy is used to mean that it acts as a Proxy for certain operations.

Proxy basic application:

const data = {
  name: "luck".age: 21
};
// Array manipulation
// const data = [1,2,3,4]  
/** * get(target, propKey, receiver) intercepts reading of object properties * set(target, propKey, value, receiver) : intercepts setting of object properties * has(target, propKey) : * deleteProperty(target, propKey) : Intercepts the operation of delete Proxy [propKey] and returns a Boolean. * ownKeys (target) : interception Object. GetOwnPropertyNames (proxy), Object. GetOwnPropertySymbols (proxy), the Object. The keys (proxy), for... The in loop returns an array. This method returns the attribute names of all the target object's own attributes *... * /

let proxy = new Proxy(data, {
  / * * * *@param {*} Target: indicates the target object *@param {*} Key: attribute name *@param {*} Receiver: Proxy instance itself (optional) *@returns* /
  get(target, key, receiver) {
    const result = Reflect.get(target, key, receiver);
    // const result = target[key]
    return result;
  },
  set(target, key, val, receiver) {
    // Duplicate data is not processed
    if (val === target[key]) {
      return true;
    }
    const result = Reflect.set(target, key, val, receiver);
    // const result = target[key] = value;
    console.log("set", key, val);
    return result;
  },
  deleteProperty(target, key) {
    const result = Reflect.deleteProperty(target, key);
    console.log("delete property", key);
    returnresult; }});// get
const age = proxy.age;
// set
proxy.name = "code";
// delete
const result = delete proxy.name;
// push
proxy.push(5)

Copy the code

Reflect

  • The Reflect method corresponds to the Proxy method. As long as it is a Proxy method, the corresponding method can be found on the Reflect object
  • Make Object operations more normalized, standardized, and functional
  • Put some utility functions of Object (such as Object.defineProperty) on Reflect.
/ / the old way
'name' in data // true
delete data.name
/ / a new way
Reflect.has(data, 'name') // true
Reflect.deleteProperty(data,'name')
Copy the code

The proxy summary:

Advantages: Can avoid Object. DefineProperty problem

  • Deep listening: better performance – it does not recurse to the end but only when it gets to the level
  • Listen for array changes
  • You can listen to add and delete properties

Cons: Not compatible with all browsers, and not polyfill

Reactive simple processing:

function reactive(target = {}) {
  if (typeoftarget ! = ="object" || target == null) {
    return target;
  }

  // Proxy configuration
  const proxyConf = {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver);
      // Deep monitor
      // Performance improvement: When does get reach this level do recursion
      return reactive(result);
    },
    set(target, key, val, receiver) {
      // Duplicate data is not processed
      if (val === target[key]) {
        return true;
      }
      const ownKeys = Reflect.ownKeys(target);
      if (ownKeys.includes(key)) {
        console.log("Existing key", key);
      } else {
        console.log("新增的 key", key);
      }

      const result = Reflect.set(target, key, val, receiver);
      console.log("set", key, val);
      return result;
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key);
      console.log("delete property", key);
      return result; // Check whether the deletion is successful}};// Generate a proxy object
  const observed = new Proxy(target, proxyConf);
  return observed;
}

let data = {
  name: "luck".age: 33.address: {
    city: "sz"
  },
  arr: [1.2.3.4]};const proxyData = reactive(data);

Copy the code