目录
一个组件 从创建到销毁的过程 称为生命周期
Vue3 组合式API(setup语法糖) 是没有 beforeCreate 和 created 这两个生命周期的
| 生命周期(选项式 API) | 执行时间 |
|---|---|
| beforeCreate | setup语法糖中,没有此生命周期。 |
| created | setup语法糖中,没有此生命周期。 |
| onBeforeMount | 在组件 DOM 实际渲染挂载之前调用。在这一步中,根元素还不存在。 |
| onMounted | 在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问 |
| onBeforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前。 |
| onUpdated | DOM更新后,updated 的方法立即会调用。 |
| onBeforeUnmount | 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。 |
| onUnmounted | 卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。 |
| onErrorCaptured | 调试时使用。 |
| onRenderTracked | 调试时使用。 |
| onRenderTriggered | 调试时使用。 |
| onActivated | |
| onDeactivated |
core-main\packages\runtime-core\src\apiLifecycle.ts
createHook 函数内部,通过函数柯里化,接受不同的生命周期枚举,并调用 injectHook 方法
- export const createHook =
-
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)
这些枚举其实是 生命周期钩子函数的 简称
- 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)
- export const enum LifecycleHooks {
- BEFORE_CREATE = 'bc',
- CREATED = 'c',
- BEFORE_MOUNT = 'bm',
- MOUNTED = 'm',
- BEFORE_UPDATE = 'bu',
- UPDATED = 'u',
- BEFORE_UNMOUNT = 'bum',
- UNMOUNTED = 'um',
- DEACTIVATED = 'da',
- ACTIVATED = 'a',
- RENDER_TRIGGERED = 'rtg',
- RENDER_TRACKED = 'rtc',
- ERROR_CAPTURED = 'ec',
- SERVER_PREFETCH = 'sp'
- }
生命周期钩子函数 会被挂载到 当前组件的实例 上
通过 getCurrentInstance 获取当前组件实例,读取 生命周期钩子函数(的简称)
- const instance = getCurrentInstance();
- console.log(instance);

core-main\packages\runtime-core\src\apiLifecycle.ts
createHook 函数内部,调用了 injectHook 方法,此方法用于 注册生命周期钩子
- export function injectHook(
- type: LifecycleHooks,
- hook: Function & { __weh?: Function },
- target: ComponentInternalInstance | null = currentInstance,
- prepend: boolean = false
- ): Function | undefined {
- if (target) {
- // 创建 hook 做一个缓存
- // 如果有 hook 函数直接返回,否则创建一个新数组(也就是说她永远是一个 Function 类型的数组)
- 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".
- const wrappedHook =
- hook.__weh ||
- (hook.__weh = (...args: unknown[]) => {
- // 如果组件已经卸载,则直接 return
- 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.
- // 设置当前 target 为组件实例
- setCurrentInstance(target)
- // 执行钩子函数
- const res = callWithAsyncErrorHandling(hook, target, type, args)
- // 清空当前组件实例(释放 target)
- unsetCurrentInstance()
- // 恢复依赖收集
- resetTracking()
- return res
- })
- if (prepend) {
- hooks.unshift(wrappedHook)
- } else {
- // 添加 hook
- hooks.push(wrappedHook)
- }
- return wrappedHook
- } else if (__DEV__) {
- const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/, ''))
- warn(
- `${apiName} is called when there is no active component instance to be ` +
- `associated with. ` +
- `Lifecycle injection APIs can only be used during execution of setup().` +
- (__FEATURE_SUSPENSE__
- ? ` If you are using async setup(), make sure to register lifecycle ` +
- `hooks before the first await statement.`
- : ``)
- )
- }
- }
core-main\packages\runtime-core\src\renderer.ts
此函数展示了 onMounted onUpdated 的实现过程
- const setupRenderEffect: SetupRenderEffectFn = (
- instance,
- initialVNode,
- container,
- anchor,
- parentSuspense,
- isSVG,
- optimized
- ) => {
- const componentUpdateFn = () => {
- // 如果当前组件没挂载,就走这个判断
- if (!instance.isMounted) {
- let vnodeHook: VNodeHook | null | undefined
- const { el, props } = initialVNode
- const { bm, m, parent } = instance
- const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
-
- toggleRecurse(instance, false)
- // beforeMount hook 判断下有没有 beforeMounted 生命周期函数,有的话就执行
- if (bm) {
- invokeArrayFns(bm)
- }
- // onVnodeBeforeMount
- if (
- !isAsyncWrapperVNode &&
- (vnodeHook = props && props.onVnodeBeforeMount)
- ) {
- invokeVNodeHook(vnodeHook, parent, initialVNode)
- }
- if (
- __COMPAT__ &&
- isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
- ) {
- instance.emit('hook:beforeMount')
- }
- toggleRecurse(instance, true)
-
- // 进行渲染操作
- if (el && hydrateNode) {
- // vnode has adopted host node - perform hydration instead of mount.
- const hydrateSubTree = () => {
- if (__DEV__) {
- startMeasure(instance, `render`)
- }
- instance.subTree = renderComponentRoot(instance)
- if (__DEV__) {
- endMeasure(instance, `render`)
- }
- if (__DEV__) {
- startMeasure(instance, `hydrate`)
- }
- hydrateNode!(
- el as Node,
- instance.subTree,
- instance,
- parentSuspense,
- null
- )
- if (__DEV__) {
- endMeasure(instance, `hydrate`)
- }
- }
-
- if (isAsyncWrapperVNode) {
- ;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
- // note: we are moving the render call into an async callback,
- // which means it won't track dependencies - but it's ok because
- // a server-rendered async wrapper is already in resolved state
- // and it will never need to change.
- () => !instance.isUnmounted && hydrateSubTree()
- )
- } else {
- hydrateSubTree()
- }
- } else {
- if (__DEV__) {
- startMeasure(instance, `render`)
- }
- const subTree = (instance.subTree = renderComponentRoot(instance))
- if (__DEV__) {
- endMeasure(instance, `render`)
- }
- if (__DEV__) {
- startMeasure(instance, `patch`)
- }
- // 把 VNode 挂载到容器中(此时已经挂载完 DOM 元素了)
- patch(
- null,
- subTree,
- container,
- anchor,
- instance,
- parentSuspense,
- isSVG
- )
- if (__DEV__) {
- endMeasure(instance, `patch`)
- }
- // 此时已经可以读到 dom 了
- initialVNode.el = subTree.el
- }
- // mounted hook 判断下有没有 onMounted 生命周期函数
- if (m) {
- // queuePostRenderEffect 函数用于执行生命周期钩子
- queuePostRenderEffect(m, parentSuspense)
- }
- // onVnodeMounted
- if (
- !isAsyncWrapperVNode &&
- (vnodeHook = props && props.onVnodeMounted)
- ) {
- const scopedInitialVNode = initialVNode
- queuePostRenderEffect(
- () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
- parentSuspense
- )
- }
- if (
- __COMPAT__ &&
- isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
- ) {
- queuePostRenderEffect(
- () => instance.emit('hook:mounted'),
- parentSuspense
- )
- }
-
- // activated hook for keep-alive roots.
- // #1742 activated hook must be accessed after first render
- // since the hook may be injected by a child keep-alive
- if (
- initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
- (parent &&
- isAsyncWrapper(parent.vnode) &&
- parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
- ) {
- instance.a && queuePostRenderEffect(instance.a, parentSuspense)
- if (
- __COMPAT__ &&
- isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
- ) {
- queuePostRenderEffect(
- () => instance.emit('hook:activated'),
- parentSuspense
- )
- }
- }
- instance.isMounted = true
-
- if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
- devtoolsComponentAdded(instance)
- }
-
- // #2458: deference mount-only object parameters to prevent memleaks
- initialVNode = container = anchor = null as any
- // 如果当前组件挂载了,走这个判断,进行组件更新
- } else {
- // updateComponent
- // This is triggered by mutation of component's own state (next: null)
- // OR parent calling processComponent (next: VNode)
- let { next, bu, u, parent, vnode } = instance
- let originNext = next
- let vnodeHook: VNodeHook | null | undefined
- if (__DEV__) {
- pushWarningContext(next || instance.vnode)
- }
-
- // Disallow component effect recursion during pre-lifecycle hooks.
- toggleRecurse(instance, false)
- if (next) {
- next.el = vnode.el
- updateComponentPreRender(instance, next, optimized)
- } else {
- next = vnode
- }
- // 组件更新之前执行,此时 DOM 还没有更新
- // beforeUpdate hook
- if (bu) {
- invokeArrayFns(bu)
- }
- // onVnodeBeforeUpdate
- if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
- invokeVNodeHook(vnodeHook, parent, next, vnode)
- }
- if (
- __COMPAT__ &&
- isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
- ) {
- instance.emit('hook:beforeUpdate')
- }
- toggleRecurse(instance, true)
-
- // render
- if (__DEV__) {
- startMeasure(instance, `render`)
- }
- const nextTree = renderComponentRoot(instance)
- if (__DEV__) {
- endMeasure(instance, `render`)
- }
- const prevTree = instance.subTree
- instance.subTree = nextTree
-
- if (__DEV__) {
- startMeasure(instance, `patch`)
- }
- patch(
- prevTree,
- nextTree,
- // parent may have changed if it's in a teleport
- hostParentNode(prevTree.el!)!,
- // anchor may have changed if it's in a fragment
- getNextHostNode(prevTree),
- instance,
- parentSuspense,
- isSVG
- )
- // patch 完成之后,DOM 组件已经是最新的了
- if (__DEV__) {
- endMeasure(instance, `patch`)
- }
- next.el = nextTree.el
- if (originNext === null) {
- // self-triggered update. In case of HOC, update parent component
- // vnode el. HOC is indicated by parent instance's subTree pointing
- // to child component's vnode
- updateHOCHostEl(instance, nextTree.el)
- }
- // updated hook
- if (u) {
- queuePostRenderEffect(u, parentSuspense)
- }
- // onVnodeUpdated
- // 组件更新之后执行,此时 DOM 已经更新
- if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
- // queuePostRenderEffect 函数用于执行生命周期钩子
- queuePostRenderEffect(
- () => invokeVNodeHook(vnodeHook!, parent, next!, vnode),
- parentSuspense
- )
- }
- if (
- __COMPAT__ &&
- isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
- ) {
- queuePostRenderEffect(
- () => instance.emit('hook:updated'),
- parentSuspense
- )
- }
-
- if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
- devtoolsComponentUpdated(instance)
- }
-
- if (__DEV__) {
- popWarningContext()
- }
- }
- }
-
- // create reactive effect for rendering
- const effect = (instance.effect = new ReactiveEffect(
- componentUpdateFn,
- () => queueJob(update),
- instance.scope // track it in component's effect scope
- ))
-
- const update: SchedulerJob = (instance.update = () => effect.run())
- update.id = instance.uid
- // allowRecurse
- // #1801, #2043 component render effects should allow recursive updates
- toggleRecurse(instance, true)
-
- if (__DEV__) {
- effect.onTrack = instance.rtc
- ? e => invokeArrayFns(instance.rtc!, e)
- : void 0
- effect.onTrigger = instance.rtg
- ? e => invokeArrayFns(instance.rtg!, e)
- : void 0
- update.ownerInstance = instance
- }
-
- update()
- }
core-main\packages\runtime-core\src\renderer.ts
此函数展示了 onUnmounted 的实现过程
- const unmount: UnmountFn = (
- vnode,
- parentComponent,
- parentSuspense,
- doRemove = false,
- optimized = false
- ) => {
- const {
- type,
- props,
- ref,
- children,
- dynamicChildren,
- shapeFlag,
- patchFlag,
- dirs
- } = vnode
- // unset ref
- if (ref != null) {
- setRef(ref, null, parentSuspense, vnode, true)
- }
-
- if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
- ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode)
- return
- }
-
- const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs
- const shouldInvokeVnodeHook = !isAsyncWrapper(vnode)
-
- let vnodeHook: VNodeHook | undefined | null
- if (
- shouldInvokeVnodeHook &&
- (vnodeHook = props && props.onVnodeBeforeUnmount)
- ) {
- invokeVNodeHook(vnodeHook, parentComponent, vnode)
- }
-
- if (shapeFlag & ShapeFlags.COMPONENT) {
- // 清空当前组件 effect 收集的依赖 和 副作用函数
- unmountComponent(vnode.component!, parentSuspense, doRemove)
- } else {
- if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
- vnode.suspense!.unmount(parentSuspense, doRemove)
- return
- }
- // 执行 beforeUnmount 卸载操作
- if (shouldInvokeDirs) {
- invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
- }
-
- if (shapeFlag & ShapeFlags.TELEPORT) {
- ;(vnode.type as typeof TeleportImpl).remove(
- vnode,
- parentComponent,
- parentSuspense,
- optimized,
- internals,
- doRemove
- )
- } else if (
- dynamicChildren &&
- // #1153: fast path should not be taken for non-stable (v-for) fragments
- (type !== Fragment ||
- (patchFlag > 0 && patchFlag & PatchFlags.STABLE_FRAGMENT))
- ) {
- // fast path for block nodes: only need to unmount dynamic children.
- // 组件卸载完成后,清空当前组件下所有的子节点
- unmountChildren(
- dynamicChildren,
- parentComponent,
- parentSuspense,
- false,
- true
- )
- } else if (
- (type === Fragment &&
- patchFlag &
- (PatchFlags.KEYED_FRAGMENT | PatchFlags.UNKEYED_FRAGMENT)) ||
- (!optimized && shapeFlag & ShapeFlags.ARRAY_CHILDREN)
- ) {
- unmountChildren(children as VNode[], parentComponent, parentSuspense)
- }
-
- if (doRemove) {
- remove(vnode)
- }
- }
- // 卸载完成后,通过 queuePostRenderEffect 执行 unmounted 生命周期钩子,表示卸载完成
- if (
- (shouldInvokeVnodeHook &&
- (vnodeHook = props && props.onVnodeUnmounted)) ||
- shouldInvokeDirs
- ) {
- queuePostRenderEffect(() => {
- vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
- shouldInvokeDirs &&
- invokeDirectiveHook(vnode, null, parentComponent, 'unmounted')
- }, parentSuspense)
- }
- }