文章目录
- 前言
- 一、关键方法 callHook
- 二、详细的钩子函数说明
- 1.beforeCreate和create
- 2.beforeMount & mounted
- 注意点
- 组件(非根组件)的渲染节点
- (1)invokeInsertHook函数
- (2)insert方法
- (3)insert方法说明
- 3.beforeUpdate & updated
- (1)beforeUpdate 的执行时机是在渲染 Watcher 的 before 函数中
- (2)update 的执行时机在flushSchedulerQueue 函数调用
- (3)问题:为什么在 callUpdatedHooks 函数中,只有 vm._watcher 的回调执行完毕后,才会执行 updated 钩子函数
- 4.beforeDestroy & destroyed
- 总结
前言
生命周期就不过多说明了。主要是讲背的八股文通过源码来解释,为什么不同的钩子函数有不同的效果
附上生命周期的流程图,如下或点击官网查看:vue2官网生命周期流程图
一、关键方法 callHook
方法路径:src\core\instance\lifecycle.ts
二、详细的钩子函数说明
1.beforeCreate和create
路径: src\core\instance\init.ts
beforeCreate和create 初始化前后,其实是在初始化函数中执行初始化数据的前后,也就是data method computed watch等属性。
2.beforeMount & mounted
路径: src\core\instance\init.ts
beforeMount & mounted 挂载前后,挂载前后其实就是生成了所有的虚拟DOM。
(挂载的含义:描述dom的数据需要整理,其实就是一堆(虚拟dom)对象储存了一堆数据,然后这些数据用js的API,比如createElement,创造出dom,这个过程是挂载前的动作。 创造出的dom还没有被使用,仅仅在内存中,需要“挂载”,然后你最外层一个叫app的id的dom,挂载就是document.querySelector(‘app’).appendChild(dom),就完成挂载啦。
注意点
(1)这里对 mounted 钩子函数执行有一个判断逻辑,vm.$vnode 如果为 null,则表明这不是一次组件的初始化过程,也就是根组件的渲染通过外部 new Vue 初始化过程。那么对于组件(非根组件)的 mounted 时机在哪里?
if (vm.$vnode == null) {vm._isMounted = truecallHook(vm, 'mounted')}
组件(非根组件)的渲染节点
(1)invokeInsertHook函数
组件的 VNode patch 到 DOM 后,会执行 invokeInsertHook 函数,把 insertedVnodeQueue 里保存的钩子函数依次执行一遍,它的定义在 src/core/vdom/patch.js 中:
(2)insert方法
(3)insert方法说明
我们可以看到,insert方法中每个子组件都是在这个钩子函数中执行 mounted 钩子函数,并且我们之前分析过,insertedVnodeQueue 的添加顺序是先子后父,所以对于同步渲染的子组件而言,mounted 钩子函数的执行顺序也是先子后父。
3.beforeUpdate & updated
beforeUpdate & updated 更新前后,顾名思义,就是在数据变化前后执行的钩子函数
(1)beforeUpdate 的执行时机是在渲染 Watcher 的 before 函数中
路径:src\core\instance\lifecycle.ts
(2)update 的执行时机在flushSchedulerQueue 函数调用
路径: src\core\observer\scheduler.ts
(3)问题:为什么在 callUpdatedHooks 函数中,只有 vm._watcher 的回调执行完毕后,才会执行 updated 钩子函数
在组件 mount 的过程中,会实例化一个渲染的 Watcher 去监听 vm 上的数据变化重新渲染,这段逻辑发生在 mountComponent 函数执行的时候:
路径:src\core\instance\lifecycle.ts
在实例化 Watcher 的过程中,它的构造函数里会判断 isRenderWatcher,接着把当前 watcher 的实例赋值给 vm._watcher
路径: src\core\observer\watcher.ts
把当前 wathcer 实例 push 到 vm._watchers 中,vm._watcher 是专门用来监听 vm 上数据变化然后重新渲染的,所以它是一个渲染相关的 watcher,因此在 callUpdatedHooks 函数中,只有 vm._watcher 的回调执行完毕后,才会执行 updated 钩子函数。
4.beforeDestroy & destroyed
beforeDestroy & destroyed销毁 前后,就是$destroy方法中
路径:src\core\instance\lifecycle.ts
beforeDestroy 钩子函数的执行时机是在 $destroy 函数执行最开始的地方,接着执行了一系列的销毁动作,包括从 parent 的 $children 中删掉自身,删除 watcher,当前渲染的 VNode 执行销毁钩子函数等,执行完毕后再调用 destroy 钩子函数。
注意点:在 $destroy 的执行过程中,它又会递归调用 vm.patch(vm._vnode, null) 触发它子组件的销毁钩子函数,所以 destroy 钩子函数执行顺序是先子后父,和 mounted 过程一样。
总结
遇到过的面试题
问:create和mounted的区别?
答:create是初始化后执行的函数,mounted是挂载后执行的函数。
问:那我如何在create执行dom操作呢?
答:在create中使用setTimeout或nextTick即可
问:父子组件中的钩子函数如何执行
答:加载渲染过程: 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated父组件更新过程
父beforeUpdate->父updated销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed