vue2 vs vue3 — the difference between responsive objects, in vue2 through Object.defineproperty, in Vue3 ref (Object.defineproperty) reactive (proxy)

vue2 vs vue3 series of articles

You can browse the Vue column on the blog homepage. Related articles will be added one after another. If you have any problems or areas that can be optimized, please let me know.
Make progress together 🙂

Article directory

  • vue2 vs vue3 series of articles
  • Responsiveness of vue2.x
    • Implementation principle
    • There is a problem
    • error test code
    • test code
    • test case
  • Responsiveness of vue3.x
    • Implementation principle
    • test code
    • test case

Responsiveness of vue2.x

Implementation principle

  1. Object type: Use Object.defineProperty() to intercept the reading and modification of properties (data hijacking).
  2. Array type: Interception is implemented by overriding a series of methods for updating data. (The array change method is wrapped).
let obj={<!-- -->name:"Penk666"}
Object.defineProperty(obj,'name',{<!-- -->
get(){<!-- -->},
set(value)(){<!-- -->}
})

There is a problem

  1. When adding attributes or deleting attributes, the interface will not be updated.
  2. If you modify the array directly through subscripts, the interface will not be updated.

Error test code

There is an infinite calling problem, such as getting person.name, calling obj[i], and getting person.name again...

/**
  * vue2 implements object data hijacking
  */
var person = {<!-- --> name: 'Penk666', age: 32, job: {<!-- --> j1: {<!-- --> salary: 10000 } } };
function Observer(obj) {<!-- -->
  // Get the key name of the object
  let keys = Object.keys(obj);
  // Traverse
  for (let i of keys) {<!-- -->
    // If it is an object and not null (null is also an object), continue to listen, forming recursion
    if (typeof obj[i] == 'object' & amp; & amp; typeof obj[i] !== null) {<!-- -->
      Observer(obj[i]);
    }
    // Main method, listen to the properties of the object.
    // Disadvantage: Unable to monitor new objects obj.xxx or delete objects delete obj.a.
    Object.defineProperty(obj, i, {<!-- -->
      get() {<!-- -->
        console.log(`Data was obtained ${<!-- -->obj[i]}...`);
        return obj[i];
      },
      set(newValue) {<!-- -->
        console.log(`Data has changed, obj.${<!-- -->i}:${<!-- -->newValue}...`);
        obj[i] = newValue;
      },
      enumerable: true,
      configurable: true
    });
  }
}

Observer(person);
console.log('person:', person);
console.log('name:', person.name);

As shown below:

Solution: Use a temporary variable that acts as a closure. let value = target[key];

Test code

Here we use the class in es6 and object-oriented programming.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      /**
       * vue2 implements object data hijacking
       */
      var person = {<!-- --> name: 'Penk666', age: 32, job: {<!-- --> salary: 10000 } };
      //data hijacking
      class Observer {<!-- -->
        constructor(data) {<!-- -->
          //Hijack data during initialization
          this.observer(data);
        }
        observer(data) {<!-- -->
          if (data & amp; & amp; typeof data == 'object') {<!-- -->
            for (let key in data) {<!-- -->
              // Data[key] is passed in here to form a closure and avoid memory leaks caused by circular calls.
              this.defineReactive(data, key, data[key]);
            }
          }
        }
        defineReactive(obj, key, val) {<!-- -->
          this.observer(val);
          Object.defineProperty(obj, key, {<!-- -->
            enumerable: true,
            get() {<!-- -->
              console.log(`Object.defineProperty monitors that the data is obtained, the current value is ${<!-- -->val}...`);
              return val;
            },
            set: (newVal) => {<!-- -->
              if (val == newVal) return;
              console.log(`Object.defineProperty monitors that the data has been modified, the current value is ${<!-- -->newVal}...`);
              val = newVal;
              // Hijack data when reassigning
              this.observer(newVal);
            }
          });
        }
      }

      new Observer(person);
    </script>
  </body>
</html>


Test cases

Warm reminder: Do not use + + operation…

// get gets the attribute value
person.name;
console.log('@@');
// set sets the attribute value
person.name + = '!';
console.log('@@');
// deep object set
person.job.salary=22;
console.log('@@');

The effect is as follows:

Responsiveness of vue3.x

Advantages: You can monitor the addition and deletion of objects; no recursive implementation is required.

Implementation principle

  1. Through Proxy: intercept changes to any attributes in the object, including: reading attribute values, adding attributes, deleting attributes, etc.
  2. Through Reflect (reflection): operate on the properties of the proxy object. Although you can operate directly on obj, JS has begun to transfer the methods of Object objects to Reflect, probably for decoupling.

Test code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      /**
       * vue3 implements object data hijacking
       */
      var o = {<!-- --> a: 1, b: 2, c: {<!-- --> d: 3 } };
      var arr = [1, 2, 3];
      function Observer(obj) {<!-- -->
        return (p = new Proxy(obj, {<!-- -->
          get(target, key) {<!-- -->
            console.log(`proxy object monitors that data is obtained, current value ${<!-- -->target[key]}...`, target, key);
            return Reflect.get(target, key);
          },
          set(target, key, newValue) {<!-- -->
            console.log('The proxy object monitors that data is added or updated...', target, key, newValue);
            Reflect.set(target, key, newValue);
          },
          deleteProperty(target, key) {<!-- -->
            console.log(`The proxy object monitors that the source object deletes ${<!-- -->key}...`);
            Reflect.deleteProperty(target, key);
          }
        }));
      }
      var proxy = Observer(o);
    </script>
  </body>
</html>

Test cases

//get an attribute
proxy.a;
//Delete an attribute
delete proxy.b;
//Add a new attribute
proxy.e = 6;
//Modify deep properties
proxy.c.cc.ccc = 33;

The effect is as follows: