Vue3.0源码学习——生命周期钩子函数

2,296 阅读3分钟

「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战」。

Vue3生命周期文档

Options API 与 setup 中使用生命周期的区别

Vue3目前兼容Vue2的生命周期函数

映射关系

Options APIsetup 中使用
beforeCreate不需要
created不需要
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered
activatedonActivated
deactivatedonDeactivated
  • 关于 setup 中为什么没有 beforeCreatecreated,官方文档中有个 tip

因为 setup 是围绕 beforeCreatecreated 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

  • 前文 Vue3.0源码学习——Composition API 学习了,在 setup 定义的时候组件的实例 instance 已经创建,因此在 setup 中使用 beforeCreatecreated 钩子没有意义,也印证了官方文档

setup 中使用生命周期钩子

import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}
  • setup 中使用生命周期钩子的优势在于,不用像Vue2那样将所有同一个生命周期相关的内容都写在这个生命周期函数中,在 setup 中可以在多处使用同一个生命周期钩子函数,这也体现出了 Composition API 的优势

源码解析

这里只看setup中生命周期钩子函数

  • 如何定位源码:在源码中 ctrl + t 随意搜一个生命周期钩子函数比如 onMounted,找到源码位置 packages\runtime-core\src\apiLifecycle.ts,可以看到所有的生命周期钩子函数都是执行了同一个函数 createHook
export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
export const onMounted = createHook(LifecycleHooks.MOUNTED)
export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
export const onUpdated = createHook(LifecycleHooks.UPDATED)
export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
export const onServerPrefetch = createHook(LifecycleHooks.SERVER_PREFETCH)

生命周期枚举值 图片.png

  • createHook 函数入参传入不同的的生命周期名称,并返回另一个函数,这个函数的第一个参数 hook 要求类型是一个函数,就是用户定义在当前生命周期要执行的代码,最终会执行注入钩子函数 injectHook
export const createHook =
  <T extends Function = () => any>(lifecycle: LifecycleHooks) =>
  (hook: T, target: ComponentInternalInstance | null = currentInstance) =>
    // post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
    (!isInSSRComponentSetup || lifecycle === LifecycleHooks.SERVER_PREFETCH) &&
    injectHook(lifecycle, hook, target)
  • injectHook 接受的第三个参数 target 就是当前组件的实例,最终会在用户传入的 hook 上包裹一层,在执行到生命周期的时候进行调用
export function injectHook(
  type: LifecycleHooks,
  hook: Function & { __weh?: Function },
  target: ComponentInternalInstance | null = currentInstance,
  prepend: boolean = false
): Function | undefined {
  if (target) {
    // 拿出实例上的hooks
    const hooks = target[type] || (target[type] = [])
    // cache the error handling wrapper for injected hooks so the same hook
    // can be properly deduped by the scheduler. "__weh" stands for "with error
    // handling".
    // 对用户传入的hook进行包裹
    const wrappedHook =
      hook.__weh ||
      (hook.__weh = (...args: unknown[]) => {
        if (target.isUnmounted) {
          return
        }
        // disable tracking inside all lifecycle hooks
        // since they can potentially be called inside effects.
        pauseTracking()
        // Set currentInstance during hook invocation.
        // This assumes the hook does not synchronously trigger other hooks, which
        // can only be false when the user does something really funky.
        setCurrentInstance(target)
        // 执行时是否会出错
        const res = callWithAsyncErrorHandling(hook, target, type, args)
        unsetCurrentInstance()
        resetTracking()
        return res
      })
    if (prepend) {
      hooks.unshift(wrappedHook)
    } else {
      // 将hook存在当前组件实例上,将来执行到钩子函数式,循环调用即可
      hooks.push(wrappedHook)
    }
    return wrappedHook
  } else if (__DEV__) {
    ...
  }
}