vue dependent collection code analysis

structure

Vue$3
    __data:
        __ob__: Observer
            dep: Dep
                id: 2
                subs: []

subs placed in Watcher, when changing the data Data, Dep subs which triggers the corresponding notification in the watcher is updated.

Code

In the course of the first observer will get registration method used to "rely on the collection." Dep have a target in its closure, this object is used to store the Watcher object instance. In fact, "dependent on the collection" process is the Watcher instance stored in the corresponding object to Dep. get ways to make the current Watcher objects (Dep.target) stored in its subs in (addSub) method, when the data changes, set calls the object's notify method Dep notification within it all Wathcer objects view updates.

function defineReactive (obj, key, val) {
    /* 一个Dep类对象 */
    const dep = new Dep();
    
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
            /* 将Dep.target(即当前的Watcher对象存入dep的subs中) */
            dep.addSub(Dep.target);
            return val;         
        },
        set: function reactiveSetter (newVal) {
            if (newVal === val) return;
            /* 在set的时候触发dep的notify来通知所有的Wathcer对象更新视图 */
            dep.notify();
        }
    });
}

class Vue {
    constructor(options) {
        this._data = options.data;
        observer(this._data);
        /* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */
        new Watcher();
        /* 在这里模拟render的过程,为了触发test属性的get函数 */
        console.log('render~', this._data.test);
    }
}

Dep defined below and Watcher

class Dep {
    constructor () {
        /* 用来存放Wathcer对象的数组 */
        this.subs = [];
    }

    /* 在subs中添加一个Watcher对象 */
    addSub (sub) {
        this.subs.push(sub);
    }

    /* 通知所有Wathcer对象更新视图 */
    notify () {
        this.subs.forEach((sub) => {
            sub.update();
        })
    }
}

class Watcher {
    constructor () {
        /* 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到 */
        Dep.target = this;
    }

    /* 更新视图的方法 */
    update () {
        console.log("视图更新啦~");
    }
}

Dep.target = null;

How Dep and associated watcher on?

Ask: Why Dep.target Watcher will point to this target?

After callHook (vm, 'beforeMount'), into the mount stage, when initialization Watcher


function noop (a, b, c) {}

// lifecycle.js
let updateComponent
updateComponent = () => {
  vm._update(vm._render(), hydrating)
}

vm._watcher = new Watcher(vm, updateComponent, noop)

In the initialization function call this.get Watcher's

var Watcher = function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
  this.vm = vm;
  //...
  this.cb = cb;
  //...
  this.expression = expOrFn.toString();
  //...
  this.getter = expOrFn;
  //...
  this.value = this.lazy ? undefined : this.get();
};

Watcher.prototype.get, attention pushTarget, this time on and Dep publisher had contact, Dep's target has been set to this wacher, and each time the object is get, it will go its own Dep Push in this wacher .

// dep.js
export function pushTarget (_target: Watcher) {
  if (Dep.target) targetStack.push(Dep.target)
  Dep.target = _target
}
export function popTarget () {
  Dep.target = targetStack.pop()
}

// watcher.js
Watcher.prototype.get = function get() {
  pushTarget(this);
  var value;
  var vm = this.vm;
  //...
  value = this.getter.call(vm, vm);
  //...
  popTarget();
  this.cleanupDeps();
  //...
  return value;
};

Watcher.prototype.get above Also note this.getter.call (vm, vm), the above expression is actually performed in the vm._update (vm._render (), hydrating). Naturally calls

Calls to the vm._render () method to return a VNode, debugging found vm. $ Options.render fact,

Vue.prototype._render = function () {
  // ...
  var vm = this;
  var ref = vm.$options;
  var render = ref.render;
  vnode = render.call(vm._renderProxy, vm.$createElement);
  // ...
  return vnode
}

// 而render方法其实就是用于输出一个虚拟节点
(function anonymous(
) {
with(this){return _c('div',{attrs:{"id":"app"}},[(message + 1 > 1)?_c('div',[_v(_s(message + 1))]):_e(),_v(" "),_c('button',{on:{"click":function($event){message += 1}}},[_v("阿道夫")])])}
})

Then the results to vm._update

Vue.prototype._update = function(vnode, hydrating) {
  var vm = this;
  var prevEl = vm.$el;
  var prevVnode = vm._vnode;
  // ...
  vm._vnode = vnode;
  
  // ...
  vm.$el = vm.__patch__(prevVnode, vnode);
  
  
  // ...
};

The conclusion is mount stage initialization Watcher, and then call get after wathcer initialization, get in pushTarget (this), and to perform its own getter is content expression, the expression is vm._update(vm._render(), hydrating)therefore render function begins execution, render function that is virtual output node.

Guess you like

Origin www.cnblogs.com/everlose/p/12542023.html