前言
本文是vue2.x源码分析的第四篇,主要讲解vue实例的初始化过程init*系列!
先看调用形式
initLifecycle(vm);initEvents(vm);initRender(vm);callHook(vm, 'beforeCreate');initInjections(vm); // 在data/props之前处理注入,暂不清楚作用Unknown4.1,本节不分析initState(vm); //最主要的函数initProvide(vm); // 在data/props之后处理provide,暂不清楚作用Unknown4.2,本节不分析callHook(vm, 'created');
1、分析initLifecycle(vm),initEvents(vm),initRender(vm)
1.1、initLifecycle(vm)
function initLifecycle (vm) {var options = vm.$options;// 定位第一个非抽象的parent,记为Unknown4.1var parent = options.parent;if (parent && !options.abstract) {while (parent.$options.abstract && parent.$parent) {parent = parent.$parent;}parent.$children.push(vm);}//给vm实例添加如下属性vm.$parent = parent;vm.$root = parent ? parent.$root : vm;vm.$children = [];vm.$refs = {};vm._watcher = null;vm._inactive = null;vm._directInactive = false;vm._isMounted = false;vm._isDestroyed = false;vm._isBeingDestroyed = false;}
1.2、initEvents(vm)
function initEvents (vm) {vm._events = Object.create(null);vm._hasHookEvent = false;// 处理父元素中的events,Unknown4.2var listeners = vm.$options._parentListeners;if (listeners) {updateComponentListeners(vm, listeners);}}
1.3、initRender(vm)
function initRender (vm) {vm.$vnode = null; // the placeholder node in parent tree,在父树中的位置vm._vnode = null; // the root of the child tree,子树的根vm._staticTrees = null;//静态树var parentVnode = vm.$options._parentVnode;var renderContext = parentVnode && parentVnode.context;vm.$slots = resolveSlots(vm.$options._renderChildren, renderContext);//处理slotvm.$scopedSlots = emptyObject;// 将createElement函数绑定到该实例,参数顺序:tag, data, children, normalizationType, alwaysNormalize.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); };外部调用版}
之后,beforeCreate生命周期函数被调用:callHook(vm, ‘beforeCreate’);
2、initState(vm)
function initState (vm) {vm._watchers = []; //用于存放所用的watcher实例var opts = vm.$options;if (opts.props) {initProps(vm, opts.props); //对props各项进行验证}if (opts.methods) {initMethods(vm, opts.methods); //将methods中各项添加到vm上}if (opts.data) {initData(vm); //对data进行观测} else {observe(vm._data = {}, true /* asRootData */);}if (opts.computed) {initComputed(vm, opts.computed); //将computed属性添加到vm,并定义响应式}if (opts.watch) {initWatch(vm, opts.watch); //对watch属性进行处理...}}
2.1、initProps(vm,propsOptions)
function initProps (vm, propsOptions) {var propsData = vm.$options.propsData || {};var props = vm._props = {};// 对props中key进行缓存以减少遍历var keys = vm.$options._propKeys = [];var isRoot = !vm.$parent; //是否是根节点// 根实例的props需要被转换observerState.shouldConvert = isRoot; //observeState={isSettingProps:false,shouldConvert:false}var loop = function ( key ) {keys.push(key);var value = validateProp(key, propsOptions, propsData, vm);{if (isReservedProp[key]) { //判断是否是保留的prop(不能是key,slot,ref)warn(("\"" + key + "\" is a reserved attribute and cannot be used as component prop."),vm);}//将props的key定义为响应式,这里的匿名函数是作为customSetter用的,defineReactive$$1函数后面会分析defineReactive$$1(props, key, value, function () {if (vm.$parent && !observerState.isSettingProps) {warn("Avoid mutating a prop directly since the value will be " +"overwritten whenever the parent component re-renders. " +"Instead, use a data or computed property based on the prop's " +"value. Prop being mutated: \"" + key + "\"",vm);}});}/* 静态props在Vue.extend()时已经被代理,这里只需对实例上的props进行代理,代理的目的很简单,就是使props中的属性可以用vm.xxx访问,而不必vm.props.xxx,实现很简单,就是使用API:Object.defineProperty*/if (!(key in vm)) {proxy(vm, "_props", key);}};for (var key in propsOptions) loop( key );observerState.shouldConvert = true;}
主要分析下validateProp(key, propsOptions, propsData, vm)
//该函数就是对prop进行验证,如type、default、required、validatorfunction validateProp (key,propOptions,propsData,vm) {var prop = propOptions[key];var absent = !hasOwn(propsData, key);var value = propsData[key];// 处理布尔类型的propif (isType(Boolean, prop.type)) {if (absent && !hasOwn(prop, 'default')) {value = false;} else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) {value = true;}}if (value === undefined) {value = getPropDefaultValue(vm, prop, key);//当type为object时,default必须是函数// 观测valuevar prevShouldConvert = observerState.shouldConvert;observerState.shouldConvert = true;observe(value);observerState.shouldConvert = prevShouldConvert;}{assertProp(prop, key, value, vm, absent);//检查是否有required和validator}return value}
2.2、initMethods(vm, opts.methods)
function initMethods (vm, methods) {var props = vm.$options.props;for (var key in methods) {//遍历methods,将methods中的方法挂在vm实例上,注意这里调用了bind函数,故methods中所有方法的this都是vm对象vm[key] = methods[key] == null ? noop : bind(methods[key], vm);{if (methods[key] == null) {warn("method \"" + key + "\" has an undefined value in the component definition. " +"Did you reference the function correctly?",vm);}//检测props中是否与methods中有同名属性if (props && hasOwn(props, key)) {warn(("method \"" + key + "\" has already been defined as a prop."),vm);}}}}
2.3、initData(vm)
function initData (vm) {//这里的data通过策略合并对象变成了函数mergedInstanceDataFnvar data = vm.$options.data;data = vm._data = typeof data === 'function'? getData(data, vm) //getData就是执行mergedInstanceDataFn函数,返回data对象: data || {};//当data函数返回的不是对象时if (!isPlainObject(data)) {data = {};"development" !== 'production' && warn('data functions should return an object:\n' +'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',vm);}// 在实例vm上代理data中各项,作用同props的代理var keys = Object.keys(data);var props = vm.$options.props;var i = keys.length;while (i--) {//data中属性不能和props中同名if (props && hasOwn(props, keys[i])) {"development" !== 'production' && warn("The data property \"" + (keys[i]) + "\" is already declared as a prop. " +"Use prop default value instead.",vm);} else if (!isReserved(keys[i])) { //data中属性不能以_或$开头proxy(vm, "_data", keys[i]);}}// 对data进行观测observe(data, true /* asRootData */);}
这里主要分析observe(data, true)
//给value添加一个observer,保存在value.__ob__属性上。function observe (value, asRootData) {//不对普通类型观测if (!isObject(value)) {return}var ob;if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {ob = value.__ob__;} else if (observerState.shouldConvert &&!isServerRendering() &&(Array.isArray(value) || isPlainObject(value)) &&Object.isExtensible(value) &&!value._isVue //当有_isVue属性时,该value不会被观测) {ob = new Observer(value); //主要函数,实例化一个Observer}if (asRootData && ob) {ob.vmCount++;}return ob}
来看看Observer(value)
var Observer = function Observer (value) {this.value = value;this.dep = new Dep(); //每个observer都实例化一个Dep,用于收集依赖this.vmCount = 0;def(value, '__ob__', this); //将__ob__放在value上,值为observer对象if (Array.isArray(value)) { //当value是数组var augment = hasProto? protoAugment: copyAugment;augment(value, arrayMethods, arrayKeys);this.observeArray(value);} else {this.walk(value); //当value是对象}};
来看看walk(value)
Observer.prototype.walk = function walk (obj) {var keys = Object.keys(obj); //这里__ob__不会出现在keys里for (var i = 0; i < keys.length; i++) {defineReactive$$1(obj, keys[i], obj[keys[i]]); //对obj中每一项进行响应式定义}};
来看看defineReactive$$1(obj, keys[i], obj[keys[i]])
function defineReactive$$1 (obj,key,val,customSetter) {var dep = new Dep();var property = Object.getOwnPropertyDescriptor(obj, key);if (property && property.configurable === false) {return}// 引用预先定义的getter/settersvar getter = property && property.get;var setter = property && property.set;//val可能是对象,故继续观测var childOb = 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;if (newVal === value || (newVal !== newVal && value !== value)) {return}if ("development" !== 'production' && customSetter) {customSetter();//报错用}if (setter) {setter.call(obj, newVal);} else {val = newVal;}childOb = observe(newVal); //设置新值后,对新值进行观测dep.notify(); //触发观测器的回调或get函数}});}
2.4、initComputed(vm, opts.computed)
function initComputed (vm, computed) {var watchers = vm._computedWatchers = Object.create(null);//computed中watcher存放在vm._computedWatchersfor (var key in computed) {var userDef = computed[key];var getter = typeof userDef === 'function' ? userDef : userDef.get;{if (getter === undefined) {warn(("No getter function has been defined for computed property \"" + key + "\"."),vm);getter = noop;}}// 对computed中每一个属性创建Watcher.watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions);// 将computed中的属性定义到vm上,跟代理差不多,使vm.xx等价于vm.computed.xxif (!(key in vm)) { //computed中属性若和vm上同名会被vm上的覆盖defineComputed(vm, key, userDef);}}}
来看看Watcher(vm, getter, noop, computedWatcherOptions)
var Watcher = function Watcher (vm,expOrFn,cb,options) {this.vm = vm;vm._watchers.push(this); //vm._watchers存放所有的watcher实例// 处理options,在initComputed中,options={lazy:true},即lazy watchers;// 在initWatch中,options={user:true}if (options) {this.deep = !!options.deep;this.user = !!options.user;this.lazy = !!options.lazy;this.sync = !!options.sync;} else {this.deep = this.user = this.lazy = this.sync = false;}this.cb = cb; //存放回调函数cbthis.id = ++uid$2; // uid for batchingthis.active = true;this.dirty = this.lazy; // for lazy watchersthis.deps = [];this.newDeps = [];this.depIds = new _Set();this.newDepIds = new _Set();this.expression = expOrFn.toString(); //存放传入的expOrFnif (typeof expOrFn === 'function') {// 当expOrFn是函数,initComputed从这儿走this.getter = expOrFn;} else {// 当expOrFn是字符串,initWatch从这儿走this.getter = parsePath(expOrFn);//字符串若有分隔,只能用'.'号,不能用空格,'-'等if (!this.getter) {this.getter = function () {};"development" !== 'production' && warn("Failed watching path: \"" + expOrFn + "\" " +'Watcher only accepts simple dot-delimited paths. ' +'For full control, use a function instead.',vm);}}this.value = this.lazy //initComputed中lazy是true,故不调用get函数;initWatcher中会调用get? undefined: this.get();};
2.4、initWatch(vm, opts.watch)
function initWatch (vm, watch) {for (var key in watch) {var handler = watch[key];if (Array.isArray(handler)) { //当handler是数组时,分别调用createWatcherfor (var i = 0; i < handler.length; i++) {createWatcher(vm, key, handler[i]);}} else {createWatcher(vm, key, handler);}}}
来看看createWatcher(vm, key, handler)
function createWatcher (vm, key, handler) {var options;if (isPlainObject(handler)) { //当handler是数组时应该提供handler属性options = handler;handler = handler.handler;}if (typeof handler === 'string') { //当handler是字符串,从vm上找同名属性handler = vm[handler];}vm.$watch(key, handler, options); //主要函数}
来看看vm.$watch(key,handler,options)
Vue.prototype.$watch = function (expOrFn,cb,options) {var vm = this;options = options || {};options.user = true; //user表示是否是用户自定义的watchervar watcher = new Watcher(vm, expOrFn, cb, options); //回到2.3中再看一遍,会发现最后调用get函数if (options.immediate) {cb.call(vm, watcher.value);}return function unwatchFn () { //返回解除watcher的函数watcher.teardown();}};
来看看get函数
Watcher.prototype.get = function get () {pushTarget(this);//内部执行了Dep.target = this,并将this存入栈targetStack;var value;var vm = this.vm;if (this.user) {try {value = this.getter.call(vm, vm);//调用getter,在initWatch中即调用如下函数:// function (obj) { //这里的obj=vm// for (var i = 0; i < segments.length; i++) { //segments是对观测表达式进行的分割// if (!obj) { return }// obj = obj[segments[i]]; //当segments.length>1时,第一次从vm上找同名属性返回,后面就从obj上找,例如// //当观测表达式是'article.title',那么segments=['article','title']// //先找vm.article,再在article上找title作为结果返回// }// return obj// }} catch (e) {handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));}} else {value = this.getter.call(vm, vm);}// "touch" every property so they are all tracked as// dependencies for deep watchingif (this.deep) { //是否进行深度观测traverse(value);}popTarget(); //从targetStack栈中弹出this,将Deep.target仍为thisthis.cleanupDeps(); //暂不清楚,Unknown4.1return value};
当initState调用完成后,created生命周期函数被调用:callHook(vm, ‘created’);
3、小结
由于还没到渲染环节,故本节中定义的响应式属性还不能起作用,不太好理解,下一节将会看到
这些响应式属性是怎么起作用的
