vue生命周期
对照一个简单的例子,来看初始化的时候都发生了什么事。
<div id="app"> <p>{{message}}</p> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { message: '第一个vue实例' } }) </script>
先从构造函数看起,构造函数在src/core/instance/index.js
function Vue (options) { //Vue构造函数 if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) } //构造函数里判断如果不是生产环境并且没有使用new关键字的话,就打印一个警告 //然后执行_init()方法
可以看到最后执行了_init()方法,_init()方法在src/core/instance/init.js
Vue.prototype._init
export function initMixin (Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { const vm: Component = this //将新实例存在vm里 // a uid vm._uid = uid++ //为vm添加一个_uid属性 let startTag, endTag //开始标记和结束标记,用于计算性能 /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` //开始标记和结束标记用模板字符串生成,以vm._uid为基础来加以区别 mark(startTag) //使用src/core/util/perf.js里定义好的mark方法创建开始标记 //mark方法用于创建性能标记 } // a flag to avoid this being observed vm._isVue = true //_isVue标记用于避免vm被监听 // merge options console.log(options,1) console.log('\n\n\n\n\n\n') if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) //内部组件初始化 } else { console.log(2) vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) //mergeOptions用于按照策略合并option //将合并好的option存为vm的属性$options //resolveConstructorOptions方法的参数是vm.constructor,其实就是Vue构造函数本身 //这里就是将Vue构造函数上的options和用户传进来的options合并然后存成实例vm的$options属性 } console.log(vm.$options,3) console.log('\n\n\n\n\n\n') /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm } //如果是开发环境,vm._renderProxy就是一个proxy对象;生产环境vm._renderProxy就是实例自身 // expose real self vm._self = vm //_self就是实例自身 initLifecycle(vm) //initLifecycle,给vm实例添加一些生命周期标识,添加$parent,$root,$children,$refs,属性 initEvents(vm) //initEvents,初始化事件相关属性 initRender(vm) //initRender(vm),给实例添加虚拟dom相关属性 callHook(vm, 'beforeCreate') //调用生命周期beforeCreate initInjections(vm) // resolve injections before data/props //在data和props初始化前处理注入的依赖 initState(vm) //初始化关于数据的属性,props,methods,data,computed,watch initProvide(vm) // resolve provide after data/props //在data和props初始化之后处理提供依赖 callHook(vm, 'created') //调用生命周期created /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false) //给实例添加一个_name属性<Anonymous> mark(endTag) //mark创建结束标记 measure(`vue ${vm._name} init`, startTag, endTag) //计算初始化的时间 } if (vm.$options.el) { vm.$mount(vm.$options.el) }//如果实例的options里有el参数,就去挂载vue到DOM元素上 } }
这其中vm.$options是实例上的初始化选项,它是由用户传入的options和Vue构造函数上的options合并而成,使用mergeOptions这个方法合并。
如果打印一下合并之前和合并之后的options就可以看到合并之后多添加了什么属性:
console.log(options,1) if (options && options._isComponent) { initInternalComponent(vm, options) } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } console.log(vm.$options,3)
合并之前的options就是用户传入的:
{ data: {message: "hello world"}, el: "#app" }
合并之后增加了很多属性:
{ data: mergedInstanceDataFn(), el: "#app", components: { __proto__: { KeepAlive:{...}, Transition: {...}, TransitionGroup: {...}, } }, directives: { __proto__: { model: {...}, show: {...} } }, filters: {}, render: anonymous(), _base: Vue, staticRenderFns: [] }