• Vue3 学习笔记 —— 生命周期


    目录

    Vue3 生命周期是什么

    Vue3 生命周期源码

    createHook 函数

    源码位置

    函数作用

    枚举 LifecycleHooks

    从组件实例上获取生命周期

    injectHook 函数

    源码位置

    函数作用

    componentUpdateFn 函数

    源码位置

    函数作用

    unmount 函数

    源码位置

    函数作用


    Vue3 生命周期是什么

    一个组件 从创建到销毁的过程 称为生命周期

    Vue3 组合式API(setup语法糖) 是没有 beforeCreate 和 created 这两个生命周期的

    生命周期(选项式 API)执行时间
    beforeCreatesetup语法糖中,没有此生命周期。
    createdsetup语法糖中,没有此生命周期。
    onBeforeMount在组件 DOM 实际渲染挂载之前调用。在这一步中,根元素还不存在。
    onMounted在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
    onBeforeUpdate数据更新时调用,发生在虚拟 DOM 打补丁之前。
    onUpdatedDOM更新后,updated 的方法立即会调用。
    onBeforeUnmount在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
    onUnmounted卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
    onErrorCaptured调试时使用。
    onRenderTracked调试时使用。
    onRenderTriggered调试时使用。
    onActivated
    onDeactivated

    Vue3 生命周期源码

    GitHub - vuejs/core: 🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web. - GitHub - vuejs/core: 🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.https://github.com/vuejs/core.git

    createHook 函数

    源码位置

    core-main\packages\runtime-core\src\apiLifecycle.ts

    函数作用

    createHook 函数内部,通过函数柯里化,接受不同的生命周期枚举,并调用 injectHook 方法

    1. export const createHook =
    2. extends Function = () => any>(lifecycle: LifecycleHooks) =>
    3. (hook: T, target: ComponentInternalInstance | null = currentInstance) =>
    4. // post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
    5. (!isInSSRComponentSetup || lifecycle === LifecycleHooks.SERVER_PREFETCH) &&
    6. injectHook(lifecycle, hook, target)

    枚举 LifecycleHooks

    这些枚举其实是 生命周期钩子函数的 简称

    1. export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
    2. export const onMounted = createHook(LifecycleHooks.MOUNTED)
    3. export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
    4. export const onUpdated = createHook(LifecycleHooks.UPDATED)
    5. export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
    6. export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
    7. export const onServerPrefetch = createHook(LifecycleHooks.SERVER_PREFETCH)
    1. export const enum LifecycleHooks {
    2. BEFORE_CREATE = 'bc',
    3. CREATED = 'c',
    4. BEFORE_MOUNT = 'bm',
    5. MOUNTED = 'm',
    6. BEFORE_UPDATE = 'bu',
    7. UPDATED = 'u',
    8. BEFORE_UNMOUNT = 'bum',
    9. UNMOUNTED = 'um',
    10. DEACTIVATED = 'da',
    11. ACTIVATED = 'a',
    12. RENDER_TRIGGERED = 'rtg',
    13. RENDER_TRACKED = 'rtc',
    14. ERROR_CAPTURED = 'ec',
    15. SERVER_PREFETCH = 'sp'
    16. }

     

    从组件实例上获取生命周期

    生命周期钩子函数 会被挂载到 当前组件的实例 上

    通过 getCurrentInstance 获取当前组件实例,读取 生命周期钩子函数(的简称)

    1. const instance = getCurrentInstance();
    2. console.log(instance);

     

    injectHook 函数

    源码位置

    core-main\packages\runtime-core\src\apiLifecycle.ts

    函数作用

    createHook 函数内部,调用了 injectHook 方法,此方法用于 注册生命周期钩子

    1. export function injectHook(
    2. type: LifecycleHooks,
    3. hook: Function & { __weh?: Function },
    4. target: ComponentInternalInstance | null = currentInstance,
    5. prepend: boolean = false
    6. ): Function | undefined {
    7. if (target) {
    8. // 创建 hook 做一个缓存
    9. // 如果有 hook 函数直接返回,否则创建一个新数组(也就是说她永远是一个 Function 类型的数组)
    10. const hooks = target[type] || (target[type] = [])
    11. // cache the error handling wrapper for injected hooks so the same hook
    12. // can be properly deduped by the scheduler. "__weh" stands for "with error
    13. // handling".
    14. const wrappedHook =
    15. hook.__weh ||
    16. (hook.__weh = (...args: unknown[]) => {
    17. // 如果组件已经卸载,则直接 return
    18. if (target.isUnmounted) {
    19. return
    20. }
    21. // disable tracking inside all lifecycle hooks
    22. // since they can potentially be called inside effects.
    23. // 否则就会停止依赖收集,避免重复收集依赖(因为组件初始化的时候,已经收集过依赖了)
    24. pauseTracking()
    25. // Set currentInstance during hook invocation.
    26. // This assumes the hook does not synchronously trigger other hooks, which
    27. // can only be false when the user does something really funky.
    28. // 设置当前 target 为组件实例
    29. setCurrentInstance(target)
    30. // 执行钩子函数
    31. const res = callWithAsyncErrorHandling(hook, target, type, args)
    32. // 清空当前组件实例(释放 target)
    33. unsetCurrentInstance()
    34. // 恢复依赖收集
    35. resetTracking()
    36. return res
    37. })
    38. if (prepend) {
    39. hooks.unshift(wrappedHook)
    40. } else {
    41. // 添加 hook
    42. hooks.push(wrappedHook)
    43. }
    44. return wrappedHook
    45. } else if (__DEV__) {
    46. const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/, ''))
    47. warn(
    48. `${apiName} is called when there is no active component instance to be ` +
    49. `associated with. ` +
    50. `Lifecycle injection APIs can only be used during execution of setup().` +
    51. (__FEATURE_SUSPENSE__
    52. ? ` If you are using async setup(), make sure to register lifecycle ` +
    53. `hooks before the first await statement.`
    54. : ``)
    55. )
    56. }
    57. }

    componentUpdateFn 函数

    源码位置

    core-main\packages\runtime-core\src\renderer.ts

    函数作用

    此函数展示了 onMounted onUpdated 的实现过程

    1. const setupRenderEffect: SetupRenderEffectFn = (
    2. instance,
    3. initialVNode,
    4. container,
    5. anchor,
    6. parentSuspense,
    7. isSVG,
    8. optimized
    9. ) => {
    10. const componentUpdateFn = () => {
    11. // 如果当前组件没挂载,就走这个判断
    12. if (!instance.isMounted) {
    13. let vnodeHook: VNodeHook | null | undefined
    14. const { el, props } = initialVNode
    15. const { bm, m, parent } = instance
    16. const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
    17. toggleRecurse(instance, false)
    18. // beforeMount hook 判断下有没有 beforeMounted 生命周期函数,有的话就执行
    19. if (bm) {
    20. invokeArrayFns(bm)
    21. }
    22. // onVnodeBeforeMount
    23. if (
    24. !isAsyncWrapperVNode &&
    25. (vnodeHook = props && props.onVnodeBeforeMount)
    26. ) {
    27. invokeVNodeHook(vnodeHook, parent, initialVNode)
    28. }
    29. if (
    30. __COMPAT__ &&
    31. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
    32. ) {
    33. instance.emit('hook:beforeMount')
    34. }
    35. toggleRecurse(instance, true)
    36. // 进行渲染操作
    37. if (el && hydrateNode) {
    38. // vnode has adopted host node - perform hydration instead of mount.
    39. const hydrateSubTree = () => {
    40. if (__DEV__) {
    41. startMeasure(instance, `render`)
    42. }
    43. instance.subTree = renderComponentRoot(instance)
    44. if (__DEV__) {
    45. endMeasure(instance, `render`)
    46. }
    47. if (__DEV__) {
    48. startMeasure(instance, `hydrate`)
    49. }
    50. hydrateNode!(
    51. el as Node,
    52. instance.subTree,
    53. instance,
    54. parentSuspense,
    55. null
    56. )
    57. if (__DEV__) {
    58. endMeasure(instance, `hydrate`)
    59. }
    60. }
    61. if (isAsyncWrapperVNode) {
    62. ;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
    63. // note: we are moving the render call into an async callback,
    64. // which means it won't track dependencies - but it's ok because
    65. // a server-rendered async wrapper is already in resolved state
    66. // and it will never need to change.
    67. () => !instance.isUnmounted && hydrateSubTree()
    68. )
    69. } else {
    70. hydrateSubTree()
    71. }
    72. } else {
    73. if (__DEV__) {
    74. startMeasure(instance, `render`)
    75. }
    76. const subTree = (instance.subTree = renderComponentRoot(instance))
    77. if (__DEV__) {
    78. endMeasure(instance, `render`)
    79. }
    80. if (__DEV__) {
    81. startMeasure(instance, `patch`)
    82. }
    83. // 把 VNode 挂载到容器中(此时已经挂载完 DOM 元素了)
    84. patch(
    85. null,
    86. subTree,
    87. container,
    88. anchor,
    89. instance,
    90. parentSuspense,
    91. isSVG
    92. )
    93. if (__DEV__) {
    94. endMeasure(instance, `patch`)
    95. }
    96. // 此时已经可以读到 dom 了
    97. initialVNode.el = subTree.el
    98. }
    99. // mounted hook 判断下有没有 onMounted 生命周期函数
    100. if (m) {
    101. // queuePostRenderEffect 函数用于执行生命周期钩子
    102. queuePostRenderEffect(m, parentSuspense)
    103. }
    104. // onVnodeMounted
    105. if (
    106. !isAsyncWrapperVNode &&
    107. (vnodeHook = props && props.onVnodeMounted)
    108. ) {
    109. const scopedInitialVNode = initialVNode
    110. queuePostRenderEffect(
    111. () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
    112. parentSuspense
    113. )
    114. }
    115. if (
    116. __COMPAT__ &&
    117. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
    118. ) {
    119. queuePostRenderEffect(
    120. () => instance.emit('hook:mounted'),
    121. parentSuspense
    122. )
    123. }
    124. // activated hook for keep-alive roots.
    125. // #1742 activated hook must be accessed after first render
    126. // since the hook may be injected by a child keep-alive
    127. if (
    128. initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
    129. (parent &&
    130. isAsyncWrapper(parent.vnode) &&
    131. parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
    132. ) {
    133. instance.a && queuePostRenderEffect(instance.a, parentSuspense)
    134. if (
    135. __COMPAT__ &&
    136. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
    137. ) {
    138. queuePostRenderEffect(
    139. () => instance.emit('hook:activated'),
    140. parentSuspense
    141. )
    142. }
    143. }
    144. instance.isMounted = true
    145. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
    146. devtoolsComponentAdded(instance)
    147. }
    148. // #2458: deference mount-only object parameters to prevent memleaks
    149. initialVNode = container = anchor = null as any
    150. // 如果当前组件挂载了,走这个判断,进行组件更新
    151. } else {
    152. // updateComponent
    153. // This is triggered by mutation of component's own state (next: null)
    154. // OR parent calling processComponent (next: VNode)
    155. let { next, bu, u, parent, vnode } = instance
    156. let originNext = next
    157. let vnodeHook: VNodeHook | null | undefined
    158. if (__DEV__) {
    159. pushWarningContext(next || instance.vnode)
    160. }
    161. // Disallow component effect recursion during pre-lifecycle hooks.
    162. toggleRecurse(instance, false)
    163. if (next) {
    164. next.el = vnode.el
    165. updateComponentPreRender(instance, next, optimized)
    166. } else {
    167. next = vnode
    168. }
    169. // 组件更新之前执行,此时 DOM 还没有更新
    170. // beforeUpdate hook
    171. if (bu) {
    172. invokeArrayFns(bu)
    173. }
    174. // onVnodeBeforeUpdate
    175. if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
    176. invokeVNodeHook(vnodeHook, parent, next, vnode)
    177. }
    178. if (
    179. __COMPAT__ &&
    180. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
    181. ) {
    182. instance.emit('hook:beforeUpdate')
    183. }
    184. toggleRecurse(instance, true)
    185. // render
    186. if (__DEV__) {
    187. startMeasure(instance, `render`)
    188. }
    189. const nextTree = renderComponentRoot(instance)
    190. if (__DEV__) {
    191. endMeasure(instance, `render`)
    192. }
    193. const prevTree = instance.subTree
    194. instance.subTree = nextTree
    195. if (__DEV__) {
    196. startMeasure(instance, `patch`)
    197. }
    198. patch(
    199. prevTree,
    200. nextTree,
    201. // parent may have changed if it's in a teleport
    202. hostParentNode(prevTree.el!)!,
    203. // anchor may have changed if it's in a fragment
    204. getNextHostNode(prevTree),
    205. instance,
    206. parentSuspense,
    207. isSVG
    208. )
    209. // patch 完成之后,DOM 组件已经是最新的了
    210. if (__DEV__) {
    211. endMeasure(instance, `patch`)
    212. }
    213. next.el = nextTree.el
    214. if (originNext === null) {
    215. // self-triggered update. In case of HOC, update parent component
    216. // vnode el. HOC is indicated by parent instance's subTree pointing
    217. // to child component's vnode
    218. updateHOCHostEl(instance, nextTree.el)
    219. }
    220. // updated hook
    221. if (u) {
    222. queuePostRenderEffect(u, parentSuspense)
    223. }
    224. // onVnodeUpdated
    225. // 组件更新之后执行,此时 DOM 已经更新
    226. if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
    227. // queuePostRenderEffect 函数用于执行生命周期钩子
    228. queuePostRenderEffect(
    229. () => invokeVNodeHook(vnodeHook!, parent, next!, vnode),
    230. parentSuspense
    231. )
    232. }
    233. if (
    234. __COMPAT__ &&
    235. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
    236. ) {
    237. queuePostRenderEffect(
    238. () => instance.emit('hook:updated'),
    239. parentSuspense
    240. )
    241. }
    242. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
    243. devtoolsComponentUpdated(instance)
    244. }
    245. if (__DEV__) {
    246. popWarningContext()
    247. }
    248. }
    249. }
    250. // create reactive effect for rendering
    251. const effect = (instance.effect = new ReactiveEffect(
    252. componentUpdateFn,
    253. () => queueJob(update),
    254. instance.scope // track it in component's effect scope
    255. ))
    256. const update: SchedulerJob = (instance.update = () => effect.run())
    257. update.id = instance.uid
    258. // allowRecurse
    259. // #1801, #2043 component render effects should allow recursive updates
    260. toggleRecurse(instance, true)
    261. if (__DEV__) {
    262. effect.onTrack = instance.rtc
    263. ? e => invokeArrayFns(instance.rtc!, e)
    264. : void 0
    265. effect.onTrigger = instance.rtg
    266. ? e => invokeArrayFns(instance.rtg!, e)
    267. : void 0
    268. update.ownerInstance = instance
    269. }
    270. update()
    271. }

    unmount 函数

    源码位置

    core-main\packages\runtime-core\src\renderer.ts

    函数作用

    此函数展示了 onUnmounted 的实现过程

    1. const unmount: UnmountFn = (
    2. vnode,
    3. parentComponent,
    4. parentSuspense,
    5. doRemove = false,
    6. optimized = false
    7. ) => {
    8. const {
    9. type,
    10. props,
    11. ref,
    12. children,
    13. dynamicChildren,
    14. shapeFlag,
    15. patchFlag,
    16. dirs
    17. } = vnode
    18. // unset ref
    19. if (ref != null) {
    20. setRef(ref, null, parentSuspense, vnode, true)
    21. }
    22. if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
    23. ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode)
    24. return
    25. }
    26. const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs
    27. const shouldInvokeVnodeHook = !isAsyncWrapper(vnode)
    28. let vnodeHook: VNodeHook | undefined | null
    29. if (
    30. shouldInvokeVnodeHook &&
    31. (vnodeHook = props && props.onVnodeBeforeUnmount)
    32. ) {
    33. invokeVNodeHook(vnodeHook, parentComponent, vnode)
    34. }
    35. if (shapeFlag & ShapeFlags.COMPONENT) {
    36. // 清空当前组件 effect 收集的依赖 和 副作用函数
    37. unmountComponent(vnode.component!, parentSuspense, doRemove)
    38. } else {
    39. if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
    40. vnode.suspense!.unmount(parentSuspense, doRemove)
    41. return
    42. }
    43. // 执行 beforeUnmount 卸载操作
    44. if (shouldInvokeDirs) {
    45. invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
    46. }
    47. if (shapeFlag & ShapeFlags.TELEPORT) {
    48. ;(vnode.type as typeof TeleportImpl).remove(
    49. vnode,
    50. parentComponent,
    51. parentSuspense,
    52. optimized,
    53. internals,
    54. doRemove
    55. )
    56. } else if (
    57. dynamicChildren &&
    58. // #1153: fast path should not be taken for non-stable (v-for) fragments
    59. (type !== Fragment ||
    60. (patchFlag > 0 && patchFlag & PatchFlags.STABLE_FRAGMENT))
    61. ) {
    62. // fast path for block nodes: only need to unmount dynamic children.
    63. // 组件卸载完成后,清空当前组件下所有的子节点
    64. unmountChildren(
    65. dynamicChildren,
    66. parentComponent,
    67. parentSuspense,
    68. false,
    69. true
    70. )
    71. } else if (
    72. (type === Fragment &&
    73. patchFlag &
    74. (PatchFlags.KEYED_FRAGMENT | PatchFlags.UNKEYED_FRAGMENT)) ||
    75. (!optimized && shapeFlag & ShapeFlags.ARRAY_CHILDREN)
    76. ) {
    77. unmountChildren(children as VNode[], parentComponent, parentSuspense)
    78. }
    79. if (doRemove) {
    80. remove(vnode)
    81. }
    82. }
    83. // 卸载完成后,通过 queuePostRenderEffect 执行 unmounted 生命周期钩子,表示卸载完成
    84. if (
    85. (shouldInvokeVnodeHook &&
    86. (vnodeHook = props && props.onVnodeUnmounted)) ||
    87. shouldInvokeDirs
    88. ) {
    89. queuePostRenderEffect(() => {
    90. vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
    91. shouldInvokeDirs &&
    92. invokeDirectiveHook(vnode, null, parentComponent, 'unmounted')
    93. }, parentSuspense)
    94. }
    95. }


     

  • 相关阅读:
    LeetCode-409. Longest Palindrome [C++][Java]
    JAVA毕业设计考研经网站系统计算机源码+lw文档+系统+调试部署+数据库
    Nginx Note(01)——Nginx简介、优点和用途
    【构建ML驱动的应用程序】第 8 章 :部署模型时的注意事项
    JNI中javah命令的使用,生成.h的头文件
    react循环实现及key的作用
    【精简】Web API--DOM获取元素
    卷积神经网络的常用改进
    Element Plus el-form表单自定义插槽如何使用
    Spring
  • 原文地址:https://blog.csdn.net/Lyrelion/article/details/126900694