callHook
src/instance/lifecycle.js
根据传⼊的字符串 hook ,去拿到 vm.$options[hook] 对应的回调函数数组,然后遍历执⾏,执⾏的时候把 vm 作为函数执⾏的上下⽂。
因此 callhook 函数的功能就是调⽤某个⽣命周期钩⼦注册的所有回调函数。
beforeCreate & created
在初始化时 src/instance/init.js
beforeCreate 和 created 的钩⼦调⽤是在 initState 的前后, 在beforeCreate前确立了父子关系和添加事件等操作。initState初始化了 props 、 data 、 methods 、 watch 、 computed 等属性。那么显然 beforeCreate 的钩⼦函数就拿不到这些东西,而created就可以。在这俩个钩⼦函数执⾏的时候,并没有渲染 DOM,所以我们也访问不了DOM。
beforeMount & mounted
- 在挂载时 src/core/instance/lifecycle.js
- 子组件执行mounted时机 执行patch时 src/core/vdom/patch.js
在patch中通过createElm创建节点是传入了insertedVnodeQueue空数组。
- createElm:如果遇见组件标签则创建组件初始化组件
组件初始化完后,这里可以清楚看到往insertedVnodeQueue数组中添加当前的组件vnode,因为是深度遍历执行所以insertedVnodeQueue中第一个vnode永远是子组件而往后才是子组件的父组件。
- createElm:如果不是组件,创建完孩子节点后判断当前vnode是否存在data属性。如果存在执行invokeCreateHooks。
这里判断data中是否存在hook,如果存在并且存在insert钩子,则把当前的组件vnode插入到insertedVnodeQueue数组中。
- path函数最后
在patch函数最后执行了invokeInsertHook函数,函数遍历insertedVnodeQueue数组执行组件的insert钩子。
- 创建组件时插入的钩子insert src/core/vdom/create-component.js
可以看到当组件没有mounted时执行mounted钩子。
- 结论:插入队列过程是先子后父,子会先插入队列中,后面才是父组件,所以beforeMounte是先父后子,Mounted执行顺序是先子后父
beforeUpdate & updated
beforeupdate src/core/instance/lifecycle.js
在组件挂载时创建了组件的渲染Watcher,beforeUpdate 的执⾏时机是在渲染 Watcher 的 before函数,这⾥有个判断,在组件已经 mounted 并且组件没有destroyed,才会去调⽤这个钩⼦函数。flushSchedulerQueue是在我们修改了值触发了setter,将值订阅的watcher丢入队列中最后遍历执行watcher.before()。
updated 数据变化时触发 src/core/observer/scheduler.js
update 的执⾏时机是在 flushSchedulerQueue 函数调⽤的时候,updatedQueu是wathcer 数组的拷贝副本,在 callUpdatedHooks 函数中,它对这些watcher进行遍历,只有满⾜当前 watcher 为渲染watcher以及组件已经 mounted并且组件没有destoryed 这三个条件,才会执⾏ updated 钩⼦函数。
beforeDestroy & destroyed
beforeDestory src/core/instance/lifecycle.js 先父后子
destory src/core/instance/lifecycle.js 先子后父
总结
结论:执行顺序
parent beforeCreate
parent created
parent beforeMount
child beforeCreate
child created
child beforeMount
child mounted
parent mounted
parent beforeDestroy
child beforeDestroy
child destroyed
parent destroyed