Let’s first look at the implementation in vue2. X. For simplicity, we don’t consider multilevel nesting, nor arrays

Implementation in vue2. X

This method is essentially a new Watcher(data, key, callback) method, in which all attributes in the data are converted to listener objects before the call, mainly using Object.defineProperty.

class Watcher{
    constructor(data, key, cb){

    }
}
// Convert to listener
function observe(data){
    new Observer(data)
}

// Modify getters and setters for data
function defineReactive(obj, key){
    let value = obj[key];
    Object.defineProperty(obj, key, {
        enumerable: true.configurable: true,
        get(){
            return value;
        },
        set(newVal){
            value = newVal
        }
    })
}
Copy the code

The implementation of the Observer is simple

class Observer {
    constructor(data){
        this.walk(data);
    }

    walk(data){
        for(var key in data) {
            // Nesting is not considered here, otherwise walk would be called recursively
            defineReactive(data, key)
        }
    }
}
Copy the code

Now how do you relate watcher to getter/setter? Vue’s method is to add a dependent class: Dep

class Watcher{
    constructor(data, key, cb){
        this.cb = cb;
        Dep.target = this; // Assign to target every time you create a new watcher
        data[key];// Call the getter, execute addSub, pass target to the corresponding deP; This is the implementation nature of VUE}}class Dep {
    constructor() {this.subs = [];
    }
    addSub(sub){
        this.subs.push(sub);
    }
    notify(){
        this.subs.forEach(sub= > sub.cb())
    }
}
function defineReactive(obj, key){
    let value = obj[key];
    let dep = new Dep(); // Each attribute has a corresponding DEP, which is stored as a closure
    Object.defineProperty(obj, key, {
        enumerable: true.configurable: true,
        get(){
            dep.addSub(Dep.target)
            Dep.target = null;
            returnvalue; }, set(newVal){ value = newVal dep.notify(); }})}Copy the code

The above is the idea of VUE. There are several reasons for the new implementation of VUe3:

  1. Object.definePropertyPerformance overhead.
  2. defineReactiveAll properties of the object you want to listen to should be executed at the beginning, because that’s the only way traditional methods can convert an object to a listener.
  3. Add delete attribute problem.
  4. Another point is that this module is coupled to the VUE, and the new version can be used as a separate library.

Then let’s see how the same functionality can be implemented using a Proxy.

The implementation of the Proxy

To convert an object to a Proxy, you simply pass it to the Proxy as a parameter.

class Watcher {
    constructor(proxy, key, cb) {
        this.cb = cb;
        Dep.target = this;
        this.value = proxy[key]; }}class Dep {
    constructor() {this.subs = []
    }
    addSub(sub){
        this.subs.push(sub);
    }
    notify(newVal){
        this.subs.forEach(sub= >{ sub.cb(newVal, sub.value); sub.value = newVal; }}})const observe = (obj) = > {
    const deps = {};
    return new Proxy(obj, {
        get: function (target, key, receiver) {
            const dep = (deps[key] = deps[key] || new Dep);
            Dep.target && dep.addSub(Dep.target)
            Dep.target = null;
            return Reflect.get(target, key, receiver);
        },
        set: function (target, key, value, receiver) {
            const dep = (deps[key] = deps[key] || new Dep);
            Promise.resolve().then((a)= > {
                dep.notify(value);
            })
            return Reflect.set(target, key, value, receiver); }}); }var state = observe({x:0})
new Watcher(state, 'x'.function(n, o){
    console.log(n, o)
});
new Watcher(state, 'y'.function(n, o){
    console.log(n, o)
});
state.x = 3;
state.y = 3;
Copy the code

Maybe we only care about x and y at first, and then we don’t do anything about the other attributes, unless we add watcher and target is null at any other time

Please correct any mistakes, thank you.