把一个普通的js对象传入Vue实例作为data选项,Vue将遍历该对象的所有属性,并使用Object.defineProperty
转为getter/setter
每个组件实例都对应一个watcher
实例,会在组件渲染的过程中把涉及到的数据属性记录为依赖。之后当依赖项的setter
触发时,就会通知watcher,进而重新渲染相关的组件。
属性必须在data对象上存在才能让Vue将其转换为响应式的
对于已经创建的实例,Vue不允许动态添加根级别的响应式属性,可以用Vue.set()来添加
observe(value, asRootData)
方法, 该方法会检测传入的value
是否已经被劫持过,如果是直接返回,如果不是,会用value实例化一个Observer
实例,Observer方法中,通过Object.defineProperty
对对象属性转为getter/setter
,从而在数据发生变化时进行一系列操作。observe方法在以下阶段有执行过:
function observe (value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
var ob;
//‘__ob__’属性时,初次实例Observer时定义的。
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
...
) {
ob = new Observer(value); //实例化Observer
}
if (asRootData && ob) {
ob.vmCount++;
}
return ob
}
Observer实例
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep(); //1 订阅者容器, 用于存放所有订阅者
this.vmCount = 0;
def(value, '__ob__', this); //通过Object.defineProperty给value定义一个__ob__属性
if (Array.isArray(value)) { //数组
if (hasProto) { // var hasProto = '__proto__' in {};
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
this.walk(value);
}
};
//Observer的原型方法:
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]); //重写obj对象keys[i]属性的get和set方法
}
};
Observer.prototype.observeArray = function observeArray (items) {
for (var i = 0, l = items.length; i < l; i++) {
observe(items[i]); //对于数组的处理,每一项都要observe一次
}
};
//1 订阅者构造函数
var Dep = function Dep () {
this.id = uid++;
this.subs = []; //存放的是watcher
};
Dep.prototype.addSub = function addSub (sub) {
this.subs.push(sub);
};
Dep.prototype.removeSub = function removeSub (sub) {
remove(this.subs, sub); //从数组中删除指定项
};
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this); //当前订阅者,watcher实例,执行watcher实例的addDep方法
}
};
Dep.prototype.notify = function notify () {
...
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update(); //watcher实例,依次执行watcher实例的update方法
}
};
以上是Observer的主要源码,对传入的对象进行处理,如果已有__ob__属性,说明已经拦截处理过,直接返回__ob__属性的值即可。否则,针对传入的对象时数组还是对象,分别处理。最终都是要经过defineReactive$$1()方法来重写对象属性的get和set方法。
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
var dep = new Dep(); //新建一个订阅者容器实例
var property = Object.getOwnPropertyDescriptor(obj, key);
//如果属性是不可配置的,跳过
if (property && property.configurable === false) {
return
}
//获取get和set方法
var getter = property && property.get;
var setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
var childOb = !shallow && observe(val);
//通过Object.defineProperty()对对象的属性进行处理,主要是get和set方法的处理
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend(); //调用了depend()方法,实际是调用了当前watcher的addDep()
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) { //如果新值旧值相等,跳过
return
}
/* eslint-enable no-self-compare */
if (customSetter) { //如果有自定义的set方法,执行
customSetter();
}
if (getter && !setter) { return }
if (setter) { //执行setter方法
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify(); //关键,上面提过,dep.notify() 实际是调用了watcher的update方法,执行更新
}
});
}
Watcher
var Watcher = function Watcher (
vm,
expOrFn,
cb,
options,
isRenderWatcher
) {
this.vm = vm;
if (isRenderWatcher) {
vm._watcher = this;
}
vm._watchers.push(this);
// options
...
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(); //调用自身的get方法,把自己添加到订阅者列表中
};
//原型方法
Watcher.prototype.get = function get () {
pushTarget(this); //指定当前Watcher为Dep.target
var value;
var vm = this.vm;
try {
value = this.getter.call(vm, vm);
} catch (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
};
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); //把自身传入订阅者列表中 Dep的subs数组中
}
}
};
Watcher.prototype.cleanupDeps = function cleanupDeps () {
//清空
var i = this.deps.length;
while (i--) {
var dep = this.deps[i];
if (!this.newDepIds.has(dep.id)) {
dep.removeSub(this);
}
}
var tmp = this.depIds;
this.depIds = this.newDepIds;
this.newDepIds = tmp;
this.newDepIds.clear();
tmp = this.deps;
this.deps = this.newDeps;
this.newDeps = tmp;
this.newDeps.length = 0;
};
//重点,Watcher的update方法,当数据发生变化时,调用
Watcher.prototype.update = function update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true;
} else if (this.sync) { //如果是同步的,立即执行
this.run();
} else { //否则传入任务队列
queueWatcher(this);
}
};
//watcher的更新方法,执行传入的回调方法
Watcher.prototype.run = function run () {
if (this.active) {
var value = this.get();
if (
value !== this.value ||
isObject(value) ||
this.deep
) {
// 获取变化前的值
var oldValue = this.value;
//获取当前新值
this.value = value;
if (this.user) {
try { //执行回调
this.cb.call(this.vm, value, oldValue);
} catch (e) {
handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\""));
}
} else { //执行回调
this.cb.call(this.vm, value, oldValue);
}
}
}
};
Watcher 又是在什么地方实例化的呢
前两者主要是对vue实例中配置的watch和computed处理,同步数据的变化,最后一个是将视图和数据的绑定。
在Vuejs中,全局执行的方法主要有 initMixin(Vue), stateMixin(Vue), eventsMixin(Vue), lifecycleMixin(Vue), renderMixin(Vue), initGlobalAPI(Vue)
根据前缀基本可以理解分别是处理的哪些部分。
在initMixin(Vue)
中,主要是初始化的工作, 例如初始化,initProxy, initLifecycle, initEvents, initRender, initInjection, initState, initProvide
等,最后还执行了vm.$mount方法
,进行视图的挂载
其中initRender方法中,通过defineReactive$$1
对vue实例的$attrs
和$listeners
属性进行了拦截处理
vm.$mount
方法中通过mountComponent(this, el, hydrating)指定了挂载和视图更新方法
function mountComponent (
vm,
el,
hydrating
) {
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode; //一个空节点
{
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
);
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
);
}
}
}
callHook(vm, 'beforeMount');
var updateComponent;
/* istanbul ignore if */
if (config.performance && mark) {
updateComponent = function () {
var name = vm._name;
var id = vm._uid;
var startTag = "vue-perf-start:" + id;
var endTag = "vue-perf-end:" + id;
mark(startTag);
var vnode = vm._render();
mark(endTag);
measure(("vue " + name + " render"), startTag, endTag);
mark(startTag);
vm._update(vnode, hydrating);
mark(endTag);
measure(("vue " + name + " patch"), startTag, endTag);
};
} else {
updateComponent = function () {
vm._update(vm._render(), hydrating); //更新视图
};
}
//实例化Watcher,指定发生变化时的回调方法, 在实例已经挂载并且未销毁前每次更新都会触发beforeUpdate生命周期钩子
new Watcher(vm, updateComponent, noop, {
before: function before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
}, true /* isRenderWatcher */);
hydrating = false;
//执行vue 实例传入的mounted()方法
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm
}
vm._update大概介绍,该部分主要是对视图进行更新,通过虚拟DOM
Vue.prototype._update
方法是在,initLifecycle中定义的,执行中调用了vm.__patch__
方法;
vm.__patch__
方法中调用了patch
,patch是一个全局方法,被赋值为createPatchFunction({ nodeOps: nodeOps, modules: modules })
的返回值, 该方法就是 Virtual DOM的实现方法
综上:
vue初始化阶段,通过new Observer()实例,调用defineReactive$$1方法对对象的属性进行拦截,并重写get和set方法,set方法中如果值发生了改变,会执行订阅者列表中watcher的update方法,进行更新。
挂载dom时,会把生成的DOM树对象new Watcher, 添加到订阅者列表,当视图发生变化时,执行传入_update中的_render方法更新视图