生命周期分析

生命周期函数就是组件在初始化或者数据更新时会触发的钩子函数。

生命周期分析 - 图1

在初始化时,会调用以下代码,生命周期就是通过 callHook 调用的

  1. Vue.prototype._init = function(options) {
  2. initLifecycle(vm)
  3. initEvents(vm)
  4. initRender(vm)
  5. callHook(vm, 'beforeCreate') // 拿不到 props data
  6. initInjections(vm)
  7. initState(vm)
  8. initProvide(vm)
  9. callHook(vm, 'created')
  10. }

可以发现在以上代码中,beforeCreate 调用的时候,是获取不到 props 或者 data 中的数据的,因为这些数据的初始化都在 initState 中。

接下来会执行挂载函数

  1. export function mountComponent {
  2. callHook(vm, 'beforeMount')
  3. // ...
  4. if (vm.$vnode == null) {
  5. vm._isMounted = true
  6. callHook(vm, 'mounted')
  7. }
  8. }

beforeMount 就是在挂载前执行的,然后开始创建 VDOM 并替换成真实 DOM,最后执行 mounted 钩子。这里会有个判断逻辑,如果是外部 new Vue({}) 的话,不会存在 $vnode ,所以直接执行 mounted 钩子了。如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子。

接下来是数据更新时会调用的钩子函数

  1. function flushSchedulerQueue() {
  2. // ...
  3. for (index = 0; index < queue.length; index++) {
  4. watcher = queue[index]
  5. if (watcher.before) {
  6. watcher.before() // 调用 beforeUpdate
  7. }
  8. id = watcher.id
  9. has[id] = null
  10. watcher.run()
  11. // in dev build, check and stop circular updates.
  12. if (process.env.NODE_ENV !== 'production' && has[id] != null) {
  13. circular[id] = (circular[id] || 0) + 1
  14. if (circular[id] > MAX_UPDATE_COUNT) {
  15. warn(
  16. 'You may have an infinite update loop ' +
  17. (watcher.user
  18. ? `in watcher with expression "${watcher.expression}"`
  19. : `in a component render function.`),
  20. watcher.vm
  21. )
  22. break
  23. }
  24. }
  25. }
  26. callUpdatedHooks(updatedQueue)
  27. }
  28. function callUpdatedHooks(queue) {
  29. let i = queue.length
  30. while (i--) {
  31. const watcher = queue[i]
  32. const vm = watcher.vm
  33. if (vm._watcher === watcher && vm._isMounted) {
  34. callHook(vm, 'updated')
  35. }
  36. }
  37. }

上图还有两个生命周期没有说,分别为 activateddeactivated ,这两个钩子函数是 keep-alive 组件独有的。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。

最后就是销毁组件的钩子函数了

  1. Vue.prototype.$destroy = function() {
  2. // ...
  3. callHook(vm, 'beforeDestroy')
  4. vm._isBeingDestroyed = true
  5. // remove self from parent
  6. const parent = vm.$parent
  7. if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
  8. remove(parent.$children, vm)
  9. }
  10. // teardown watchers
  11. if (vm._watcher) {
  12. vm._watcher.teardown()
  13. }
  14. let i = vm._watchers.length
  15. while (i--) {
  16. vm._watchers[i].teardown()
  17. }
  18. // remove reference from data ob
  19. // frozen object may not have observer.
  20. if (vm._data.__ob__) {
  21. vm._data.__ob__.vmCount--
  22. }
  23. // call the last hook...
  24. vm._isDestroyed = true
  25. // invoke destroy hooks on current rendered tree
  26. vm.__patch__(vm._vnode, null)
  27. // fire destroyed hook
  28. callHook(vm, 'destroyed')
  29. // turn off all instance listeners.
  30. vm.$off()
  31. // remove __vue__ reference
  32. if (vm.$el) {
  33. vm.$el.__vue__ = null
  34. }
  35. // release circular reference (#6759)
  36. if (vm.$vnode) {
  37. vm.$vnode.parent = null
  38. }
  39. }

在执行销毁操作前会调用 beforeDestroy 钩子函数,然后进行一系列的销毁操作,如果有子组件的话,也会递归销毁子组件,所有子组件都销毁完毕后才会执行根组件的 destroyed 钩子函数。