接上,watcher构造函数:
var Watcher = function Watcher (
vm,
expOrFn,
cb,
options,
isRenderWatcher
) {
this.vm = vm;
if (isRenderWatcher) {
vm._watcher = this;
}
vm._watchers.push(this);
// options
if (options) {
this.deep = !!options.deep;
this.user = !!options.user;
this.lazy = !!options.lazy;
this.sync = !!options.sync;
this.before = options.before;
} else {
this.deep = this.user = this.lazy = this.sync = false;
}
this.cb = cb;
this.id = ++uid$2; // uid for batching
this.active = true;
this.dirty = this.lazy; // for lazy watchers
this.deps = [];
this.newDeps = [];
this.depIds = new _Set();
this.newDepIds = new _Set();
this.expression = expOrFn.toString();
// parse expression for getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn;
} else {
this.getter = parsePath(expOrFn);
if (!this.getter) {
this.getter = noop;
warn(
"Failed watching path: \"" + expOrFn + "\" " +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
);
}
}
this.value = this.lazy
? undefined
: this.get();
};watcher构造函数,首先和当前vm组件相互引用,然后初始化了一些下面执行中会用到的属性,然后检查expOrFn是否符合要求,有则用之,无则警告,最后执行:
this.value = this.lazy ? undefined : this.get();
因为this.lazy为false,进入Wathcer.prototype.get方法:
Watcher.prototype.get = function get () {
pushTarget(this);
var value;
var vm = this.vm;
try {
value = this.getter.call(vm, vm);
} catch (e) {
if (this.user) {
handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value);
}
popTarget();
this.cleanupDeps();
}
return value
};这个时候我们发现他调用了pushTarget方法:
function pushTarget (target) {
targetStack.push(target);
Dep.target = target;
}pushTarget方法很简单,就是将当前watcher实例push进了一个叫做targetStack的栈中,然后将Dep.target设置为当前watcher实例的引用,仔细想想你在哪里看到过这个,Dep.target?
想起来没,就是设置data响应式数据的时候,get里的那个Dep.target,所以有了Dep.target,意味着我们要开始依赖收集了,然后代码继续走回到 Watcher.prototype.get中,调用了this.getter.call(vm, vm),这个方法其实就是我们上边说的updateComponent方法。然后走:
vm._update(vm._render(), hydrating);
这里先会执行vm._render(),hydrating是一个变量表示是否是服务端生成模式,大可不必关心,我们现在要执行的是vm._render:
Vue.prototype._render = function () {
var vm = this;
var ref = vm.$options;
var render = ref.render;
var _parentVnode = ref._parentVnode;
if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots,
vm.$scopedSlots
);
}
// set parent vnode. this allows render functions to have access
// to the data on the placeholder node.
vm.$vnode = _parentVnode;
// render self
var vnode;
try {
// There's no need to maintain a stack becaues all render fns are called
// separately from one another. Nested component's render fns are called
// when parent component is patched.
currentRenderingInstance = vm;
vnode = render.call(vm._renderProxy, vm.$createElement);
} catch (e) {
handleError(e, vm, "render");
// return error render result,
// or previous vnode to prevent render error causing blank component
/* istanbul ignore else */
if (vm.$options.renderError) {
try {
vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e);
} catch (e) {
handleError(e, vm, "renderError");
vnode = vm._vnode;
}
} else {
vnode = vm._vnode;
}
} finally {
currentRenderingInstance = null;
}
// if the returned array contains only a single node, allow it
if (Array.isArray(vnode) && vnode.length === 1) {
vnode = vnode[0];
}
// return empty vnode in case the render function errored out
if (!(vnode instanceof VNode)) {
if (Array.isArray(vnode)) {
warn(
'Multiple root nodes returned from render function. Render function ' +
'should return a single root node.',
vm
);
}
vnode = createEmptyVNode();
}
// set parent
vnode.parent = _parentVnode;
return vnode
};
}这里他主要做了几件事呢,其实就是将render函数,当前实例,当前实例的相关配置包含data那些东西取出来,因为我们_parentVnode为空,故直接走try里的render,render执行的时候是上下文为我们最开始定义的那个代理的对象,传进去了一个叫做vm.$createElement的方法,这个方法是在initRender的时候创建的,用于创建一个VNode的节点,然后进入render函数,因为render函数这个玩意啊,像我们一般人写的时候基本上不咋写,这里我们看看他长啥样子:
function anonymous() {
with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(msg))])}
}好啦,解释一下with干了个啥,相当于将this也就是vm._renderProxy加入到了当前作用域链的头,那么这个时候with里边的语句的变量可以访问到的东西第一个能访问的就是vm._renderProxy里的,然后执行_s(msg)的时候因为是一个读值操作,故而,触发了get方法,由于之前将vm.key,代理到了vm._data.key,所以首先会触发,vm.key的get方法:
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
};然后,在执行this[sourceKey][key]的时候会触发data上响应式数据的get方法,也就是这个时候开始收集依赖,并且见到了我们的Dep.target:
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
}前面说过Dep.target就是当前vue实例的watcher,然后执行dep.depend:
function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};这里又调用了watcher的addDep并把当前的dep实例传了过去:
/**
* Add a dependency to this directive.
*/
Watcher.prototype.addDep = function addDep (dep) {
var id = dep.id;
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id);
this.newDeps.push(dep);
if (!this.depIds.has(id)) {
dep.addSub(this);
}
}
};判断了几下有没有之后,最终将watcher实例加入到了dep实例的subs数组中,get执行完毕,回到render函数中。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号