Vue生命周期源码分析~
Posted 三水草肃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue生命周期源码分析~相关的知识,希望对你有一定的参考价值。
Vue实例的生命周期大致可分为四个阶段
- 初始哈阶段:为Vue实例上初始化一些属性,事件以及响应式数据
- 模板编译阶段:将模板编译成渲染函数
- 挂载阶段: 将实例挂载到指定的DOM上,即将模板渲染到真实DOM中
- 销毁阶段: 将实例自身从父组件中删除,并取消依赖追踪及事件监听器
初始化阶段:
初始化阶段所做的第一件事就是new Vue()创建一个Vue实例,那么new Vue()的内部都干了上面,我们知道,new关键字在JS中表示从一个类中实例化出一个对象来,由此可见,Vue实例上是一个类,所以new Vue()实例上执行了Vue类的构造函数
初始化阶段所作的工作也可大致分为两个部分:第一部分是new Vue(),也就是创建一个Vue实例,第二部分是为创建好的Vue实例初始化一些事件,属性,响应式数据等。
讲述第一个初始化函数initLifecycle
- ----- 初始化第一部分initLifecycle: 给实例初始化一些属性,包括以$开头的供用户使用的外部属性,也包括_开头的拱内部使用的内部属性
- ----- 初始化第二部分initEvents:初始化实例的事件系统,父组件给子组件的注册事件中,把自定义事件传给子组件,在子组件实例化的时候进行初始化,而浏览器原生事件是在父组件中吹。 也就是实例初始化阶段调用的初始化事件函数initEvents实际上初始化的是父组件在模板中使用v-on或者@注册的监听子组件触发的事件。该函数是用来初始化实例的事件系统的。
- ----- 第四个初始化函数initInjections,该函数是用来初始化实例中的inject选项的,作用:允许一个祖先组件想其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上次游关系成立的时间里始终生效。并且provide选项应该是一个对象或返回一个对象的函数,该对象包含可注入子孙的属性。在该对象中你可以使用ES5 Symbol作为key,但是只在原生支持Symbol和Reflect.ownKeys的环境下可工作
- inject选项应该是: 一个字符串数组 或 一个对象,对象的key是本地的绑定名。value是可在用的注入内容中搜索用的key或者一个对象。该对象的from属性是在可用的注入呢日中搜索用的key,default属性是降级情况下使用的value
- 父组件可以使用provide选项给自己的下游子孙组件内注入一些数据,在下游子孙组件中可以使用inject选项来接收这些数据以便为自己使用
- provide 和 inject选项绑定的数据不是响应式的
- 总结:该函数是用来初始化inject选项的,
- ----- 第五个初始化函数initState:是用来初始化实例状态的,data,props,methods,computed,watch选项,我们把这些称为实例的状态选项,也就是说initState函数就是用来初始化这些状态的,
初始化computed: 计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。
初始化watch:可以用来侦听某个已有的数据,当该数据发生变化执行对应的对调函数,
模板编译阶段
- 模板编译阶段:模板编译阶段并不是存在于Vue的所有构建版本中,它只存在于完整版本,在只包含运行的时候版本中并不存在改阶段,这是因为当使用vue-loader或者vueify的时候,vue文件内部的模板会在构建的时候预编译成渲染函数,所以是不需要编译的。从而不存在模板编译阶段,由上一步的初始化阶段直接进入下一阶段的挂载阶段。
- vue基于源码构建的有两个版本,一个是runtime only一个只包含运行是的版本,另一个是runtime+compiler一个同事包含编译器和运行时的- 完整版本,这两个版本的区别仅在于后者包含了一个编译器。
- 完整版本是包含编译器的,我们可以使用template选项进行模板编写,扁你其会自动将template选项中的模板字符串编译成函数的代码,源码中被的render函数, 如果你需要再客户端编译模板,就需要一个包含编译器的版本
// 需要编译器的版本
new Vue({
template: '<div>{{ hi }}</div>'
})
- 只包含运行时的版本: 拥有创建Vue实例,渲染并处理Virtval DOM等功能,基本上就是除去编译器外的完整代码,该版本的适用场景有两种:我们在选项中通过手写render函数去定义渲染过程,这个时候并不需要包含编译器的版本便可完整执行。没有模板编译阶段。
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
})
模板编译阶段分析:上文中说了,完整版和只包含运行时版之间的差异主要在于是否有模板编译阶段。而是否有模板编译阶段主要表现在vm.$mount方法的实现上,
- 生命周期第二阶段----模板编译阶段:Vue源码构建的两种版本:完整版本和只包含运行时版本,模板编译阶段只存在于完整版中,在只包含运行时版本中不存在该阶段。这是因为在只包含运行时版本中,当使用vue-loader或vueify时,vue文件内部的模板会在构建时预编译成渲染函数,所以是不需要编译的,从而不存在模板编译阶段。
- 然后对比了两种版本$mount方法的区别,他们的区别在于$mount方法中是否进行了模板编译,在只包含了运行时版本的$mount方法中获取道DOM元素后直接进入挂载阶段,而在完整版本中$mount方法中是先将模板进行编译,然后回过头来只包含运行时版本的$mount方法进入挂载阶段。
- 最后我们知道,分析模板编译阶段其实就是分析完整版的vm.$mount方法实现,我们将完整版的vm.$mount方法源码进行了逐行分析,知道在该阶段所作的工作就是:从用户传入的el选项和template选贤中获取的用户传入的内部或外部模板,然后将获取到的模板编译成渲染函数
挂载阶段
- ----- 挂载阶段所做的主要工作就是创建Vue实例并用其替换el选项对应的DOM元素,同时还要开启对模板中数据的监控,当数据发生变化时通知其依赖进行视图更新
挂载阶段分析:在完整版本中
m
o
u
n
t
方
法
中
将
模
板
编
译
完
成
之
后
,
会
回
过
头
来
去
调
只
包
含
运
行
版
本
的
mount方法中将模板编译完成之后,会回过头来去调只包含运行版本的
mount方法中将模板编译完成之后,会回过头来去调只包含运行版本的mount方法进入挂载阶段。所有要想分析挂载姐u但我们必须要从只包含运行时版本的$mount方法入手
总结:
- 主要工作时创建Vue实例并用其替换el选项对应的DOM元素,同时还要开启对模板中数据的监控,当数据发生变化时通知其依赖进行更新。
- 我们将挂在阶段分成两部分进行了分析: 第一部分时将模板渲染到视图上,第二部分是开启对模板中数据的监控,两部分工作都完成以后挂载阶段才算是真正的完成了
销毁阶段
- 生命周期最后一个阶段:销毁阶段,
- 实例身上的依赖包含两个部分:一部分是实例自身依赖其它数据,需要将实例自身从其它数据的依赖列表中删除。另一部分是实例内的数据对其它数据的依赖(如用户hi用的$watch创建的依赖)越需要从其它数据的依赖列表中删除实例内数据,所以删除依赖的时候需要将这两部分依赖都删除掉
- 总结:调用了实例上vm.$destory方法后,实例就进入了销毁阶段,在该阶段所做的主要工作是将当前的Vue实例从其父级实例中删除,取消当前实例上的所有依赖追踪并且移除shi’l上的所有事件监听器,并且对照源码所作的工作都进行了逐行分析
以上是关于Vue生命周期源码分析~的主要内容,如果未能解决你的问题,请参考以下文章