记录一下vue源码调试的过程!只为学习!
function Vue (options) {
if (!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
// 从这里的init开始调试!!!!
this._init(options);
}
只截取了部分源码,把我没有看到的部分暂时先舍去了。上面的init就是下面的initMixin方法里面的**_init**方法,ok,继续看下去
function initMixin (Vue) {
Vue.prototype._init = function (options) {
var vm = this;
// a uid
vm._uid = uid$3++;
// a flag to avoid this being observed
vm._isVue = true;
{
initProxy(vm);
}
// expose real self
vm._self = vm;
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
// 初始化状态,并对数据进行观察
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created')
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
};
}
第一条线:看一下initRender过程
function initRender (vm) {
vm._vnode = null; // the root of the child tree
vm._staticTrees = null; // v-once cached trees
var options = vm.$options;
var parentVnode = vm.$vnode = options._parentVnode;
var renderContext = parentVnode && parentVnode.context;
vm.$slots = resolveSlots(options._renderChildren, renderContext);
vm.$scopedSlots = emptyObject;
// 这里的_c其实是对createElement方法的使用,vue中很多通过_小写字母来代理一些原生方法
vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };
vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };
var parentData = parentVnode && parentVnode.data;
/* istanbul ignore else */
// 将$attrs和$listeners 通过defineReactive$$1 变成响应式
{
defineReactive$$1(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () {
!isUpdatingChildComponent && warn("$attrs is readonly.", vm);
}, true);
defineReactive$$1(vm, '$listeners', options._parentListeners || emptyObject, function () {
!isUpdatingChildComponent && warn("$listeners is readonly.", vm);
}, true);
}
}
第二条线:①initState,相对比较重要的一条线
function initState (vm) {
vm._watchers = [];
if (opts.data) {
// 初始化data的时候,对data进行了数据代理,和observe动作
initData(vm);
} else {
observe(vm._data = {}, true /* asRootData */);
}
}
然后到initState里面之后,在看一下②initData方法
// 初始化数据data
function initData (vm) {
var data = vm.$options.data;
// 在getData中是对data函数的调用,生成了obj对象
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {};
// 判断data函数是否返回一个对象obj,如果不返回一个obj,则报警告warn
if (!isPlainObject(data)) {
data = {};
warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
);
}
// proxy data on instance
var keys = Object.keys(data);
var props = vm.$options.props;
var methods = vm.$options.methods;
var i = keys.length;
// 通过while循环,遍历对象dataObj,并对dataObj的每一个属性通过Object.defineProperty变成响应式属性
while (i--) {
var key = keys[i];
{
if (methods && hasOwn(methods, key)) {
warn(
("Method \"" + key + "\" has already been defined as a data property."),
vm
);
}
}
if (props && hasOwn(props, key)) {
warn(
"The data property \"" + key + "\" is already declared as a prop. " +
"Use prop default value instead.",
vm
);
} else if (!isReserved(key)) {
// 代理data数据
proxy(vm, "_data", key);
}
}
// observe data
// 对数据data进行observe,如果对象__ob__上有数据,说明该对象已经有了observe,直接返回
observe(data, true /* asRootData */);
}
接着看一下③observe方法
function observe (value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
var ob;
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
// 实际上会走这个分支
ob = new Observer(value);
}
if (asRootData && ob) {
ob.vmCount++;
}
return ob
}
接下来看一下这个**④new Observer()**的过程
观察者类
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
// 给传入的data对象添加一个响应式的属性__ob__,值就是这个observer
def(value, '__ob__', this);
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
// 当value是一个array的时候,走上面,不是array,走这个walk方法
// 在walk方法里,对value的属性进行响应式属性的设置
this.walk(value);
}
};
瞅瞅这个⑤walk方法,看起来相对简单,实际上就是对data生成的obj遍历,对属性进行响应式的处理
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]);
}
};
最后贴一下这个⑥defineReactive$$1方法
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
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(obj, key, {
enumerable: true,
configurable: true,
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
},
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) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
}