一、概述
vue的生命周期包含以下几个次要阶段:
(1)初始化阶段
初始化阶段包含新建Vue实例、初始化事件、初始化生命周期、初始化注入等;
初始化阶段对应着beforeCreate()和created()事件之前的阶段;
(2)模板编译阶段
模板编译阶段对应着beforeMount()之前的阶段;
(3)挂载阶段
挂载阶段将实例挂载到指定的DOM下来,将模板渲染到实在的DOM上;
挂载阶段对应着mounted()之前的阶段;
(4)销毁阶段
销毁阶段将实例从父组件中删除。
二、初始化阶段
Ⅰ.初始化阶段,首先要做的是新建一个Vue实例,Vue类中所做的非常简略,调用了this._init(options)函数,就实现了Vue的定义。
(1)this._init()函数从何而来?
Vue源码中core/instance/init.js中,定义了一个函数initMixin(),在此函数中,给Vue的原型绑定了_init办法:
Vue.prototype._init = function (options?: Object) {//do something}
(2)_init办法中做了什么?
次要做了以下几件事:
- 合并options属性,并将合并后的值赋值给$options挂载到vue实例上;
- 初始化生命周期、事件、渲染、注入、data等;
- 在适合的工夫调用触发生命周期的钩子函数
- 在实现了所有的初始化工作后,会判断是否传入了el,进行模板编译和挂载
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
如果没有el选项,则须要用户手动挂载vm.$mount()。
(3)合并属性?
mergeOptions办法将vue实例的构造函数上的options与传入的options做合并;
有如下几个重点须要理解:
- 构造函数的options包含components、filters、directives几个空对象,附加一些内置组件;
- 对于不同类型的options,有着不一样的合并策略,通过mergeField()办法,对他们进行合并:
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
通过不同的策略,把父子options中雷同的属性进行合并;
- 对于生命周期钩子函数的合并函数,后果是将雷同生命周期钩子函数合并成数组,这样做是思考到vue的mixins注入时,有可能在同样的生命周期中注入不同的函数,而这些函数均须要触发,所以用数组保留。
(4)callHook是如何执行的?
- 首先获取属性合并中取得的对应钩子函数数组;
- 遍历该数组,将数组中的所有函数都执行一遍。
Ⅱ.初始化阶段,在实现了实例的新建之后,会调用一些初始化函数。
(1)initLifecycle()
这个办法内容非常简略,次要是将一些属性设置默认值并挂在给实例,次要有$parent、$root属性;
(2)initEvents():初始化实例的事件零碎(v-on,@…)
- initEvents(): 1、实例下面绑定_events为空对象(这里的_events是父组件绑定在以后组件上的事件)2、判断父组件上是否有注册事件(同样是父组件绑定在子组件的事件),若有,则将父组件上的事件注册到子组件上——调用updateComponentListeners();
- updateComponentListeners(): 调用了updateListeners()函数,将listeners和add/remove两个办法传入;(target先设为vm,调用函数后设为undefined是什么起因?保留对vm实例的援用)
- 在理解updateListeners函数之前,首先看看add和remove别离做了什么?
* add()调用实例的$on办法,将通过$on传入的事件传入到_events对象中{eventName:eventFunction()……}
* remove()调用实例的$off办法,通过代码能够看出$off有三种用法,第一种:不传参数,将所有事件清空;第二种:只传第一个参数,将_events对象下的该属性事件数组清空;第三种:传入两个参数,革除某个特定事件;
- updateListeners()次要做的就是将新传入的事件与原来的事件比照,有新增则调用add,原有事件列表中存在而现有不存在的则调用remove卸载事件;(createFnInvoker局部读不懂:因为一个事件可能会对应多个回调函数,所以这里做了数组的判断,多个回调函数就顺次调用。留神最初的赋值逻辑, invoker.fns = fns,每一次执行 invoker 函数都是从 invoker.fns 里取执行的回调函数,回到 updateListeners,当咱们第二次执行该函数的时候,判断如果 cur !== old,那么只须要更改 old.fns = cur 把之前绑定的 involer.fns 赋值为新的回调函数即可,并且 通过 on[name] = old保留援用关系,这样就保障了事件回调只增加一次,之后仅仅去批改它的回调函数的援用。)
- 模板解析阶段对事件的解析
模板解析时,除了解析开始标签,也解析标签中的属性等,processAttrs办法,解析不同的属性,当遇到v-on修饰符时,就会调用addHandler() 办法。
* addHandler()中次要做了什么?
首先,对于有不同修饰符的事件(capture,once等),为事件名称name增加对应的符号,做出非凡解决,以便前面生成render函数时辨认不同的事件;
接着依据 modifier.native 判断事件是一个浏览器原生事件还是自定义事件,别离对应 el.nativeEvents 和 el.events;
最初将对应的事件函数的字符串保留到对应的events[name]中去。比方:
<child @select="selectHandler" @click.native="clickHandler"></child>
造成:
el.events = {
select: {
value: ‘selectHandler’
}}
el.nativeEvents = {
click: {
value: ‘clickHandler’
}}
* 模板编译的代码生成阶段,根据上述产生的events和nativeEvents,在genData函数中生成所须要的data数据,最开始的模板中标签上注册的事件最终会被解析成用于创立元素型VNode的_c(tagName,data,children)函数中data数据中的两个对象,自定义事件对象on,浏览器原生事件nativeOn。
*上述生成的渲染函数,在new Vue阶段创立对应的组件 const listeners = data.on;data.on = data.nativeOn,这就阐明,initEvents初始化时,初始化的是父组件注册在子组件上的(应用on或者@)自定义事件,浏览器的原生事件则是在父组件内解决。
三、小结
本篇次要建立了初始化阶段所做的一些事件,包含init当中是如何合并属性的,如何调用生命周期钩子函数,以及初始化阶段调用的初始化函数,次要介绍了生命周期的初始化以及事件的初始化,其余初始化函数见《Vue源码之生命周期——初始化阶段②》。
发表回复