• Vue3 源码阅读(10):组件化 —— 实现原理


    组件是对页面中内容的一种模块化封装,这篇博客讲解 Vue 中组件化的实现方式。

    1,Vue 中组件的定义方式

    在讲解组件化的实现原理前,首先说明在 Vue 中,一个组件的本质到底是什么。Vue 中有两种形式的组件,分别是有状态组件和无状态组件,有状态组件的本质是一个对象字面量,无状态组件的本质是一个函数。也就是说,在 Vue 中,组件的本质是一个对象字面量或者一个函数,这里以有状态组件进行讲解。

    2,VNode 简介

    VNode 的本质就是一个普通的对象字面量,只不过这个对象字面量能够很好的描述真实 DOM,通过 VNode 可以渲染出页面中真实的 DOM。

    VNode 是通过组件的 render 函数创建出来的,我们平时在开发中,一般都是使用 template 字符串描述页面内容,这个模板字符串会被 Vue 的编译器编译成 render 函数,所以在 Vue 的运行时,用于描述组件渲染内容的是 render 函数。

    下面展示一下 Vue3 中 VNode 的 TypeScript 的类型定义:

    1. export interface VNode<
    2. HostNode = RendererNode,
    3. HostElement = RendererElement,
    4. ExtraProps = { [key: string]: any }
    5. > {
    6. __v_isVNode: true
    7. [ReactiveFlags.SKIP]: true
    8. type: VNodeTypes
    9. props: (VNodeProps & ExtraProps) | null
    10. key: string | number | symbol | null
    11. ref: VNodeNormalizedRef | null
    12. scopeId: string | null
    13. slotScopeIds: string[] | null
    14. children: VNodeNormalizedChildren
    15. component: ComponentInternalInstance | null
    16. dirs: DirectiveBinding[] | null
    17. transition: TransitionHooks<HostElement> | null
    18. el: HostNode | null
    19. anchor: HostNode | null // fragment anchor
    20. target: HostElement | null // teleport target
    21. targetAnchor: HostNode | null // teleport target anchor
    22. staticCount: number
    23. suspense: SuspenseBoundary | null
    24. ssContent: VNode | null
    25. ssFallback: VNode | null
    26. shapeFlag: number
    27. patchFlag: number
    28. dynamicProps: string[] | null
    29. dynamicChildren: VNode[] | null
    30. appContext: AppContext | null
    31. memo?: any[]
    32. isCompatRoot?: true
    33. ce?: (instance: ComponentInternalInstance) => void
    34. }

    VNode 对象的属性还是很多的,这里不用看这么多,先关注一下 type 属性。

    1. export interface VNode<
    2. HostNode = RendererNode,
    3. HostElement = RendererElement,
    4. ExtraProps = { [key: string]: any }
    5. > {
    6. type: VNodeTypes
    7. }
    8. export type VNodeTypes =
    9. | string
    10. | VNode
    11. | Component
    12. | typeof Text
    13. | typeof Static
    14. | typeof Comment
    15. | typeof Fragment
    16. | typeof TeleportImpl
    17. | typeof SuspenseImpl

    type 属性用于描述 VNode 的类型,VNode 的类型有很多种,这里我们看下 string 和 Component 类型,当 VNode 的 type 属性是字符串的时候,说明当前的 VNode 描述的是普通的元素,当 VNode 的 type 是 Component 的时候,说明当前的 VNode 描述的是一个组件。

    假设我们的 Vue 中有一个 MyComponent 组件,我们在一个模板字符串中使用了这个组件,代码如下所示:

    1. <template>
    2. <MyComponent>MyComponent>
    3. template>

    上面的模板字符串会被编译成一个 render 函数,render 函数执行返回一个 VNode,这个 VNode 是一个组件类型的 VNode,表明需要渲染一个组件。

    1. componentVNode = {
    2. type: {
    3. ...组件的定义对象...
    4. },
    5. ......
    6. }

    有了组件类型的 VNode,接下来看看这个组件 VNode 是如何渲染和更新的。

    3,组件的挂载和更新

    组件挂载和更新的逻辑都写在渲染器中,我们直接看源码。

    1. const patch: PatchFn = (
    2. n1,
    3. n2,
    4. container,
    5. anchor = null,
    6. parentComponent = null,
    7. parentSuspense = null,
    8. isSVG = false,
    9. slotScopeIds = null,
    10. optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
    11. ) => {
    12. // 解构获取 n2 的 type、ref、shapeFlag
    13. const { type, ref, shapeFlag } = n2
    14. // 根据 n2 Vnode 的类型进行不同的处理
    15. switch (type) {
    16. ......
    17. default:
    18. if (shapeFlag & ShapeFlags.ELEMENT) {
    19. // 处理元素节点
    20. processElement()
    21. } else if (shapeFlag & ShapeFlags.COMPONENT) {
    22. // 处理组件节点
    23. processComponent(
    24. n1,
    25. n2,
    26. container,
    27. anchor,
    28. parentComponent,
    29. parentSuspense,
    30. isSVG,
    31. slotScopeIds,
    32. optimized
    33. )
    34. } else if (__DEV__) {
    35. // 如果以上条件都不满足,并且是在开发模式下的话,则打印出相关警告:违法的 vnode 类型
    36. warn('Invalid VNode type:', type, `(${typeof type})`)
    37. }
    38. }
    39. }

    在 patch 函数中,会根据 VNode 类型的不同使用不同的函数进行处理,如果当前的 VNode 表示的是组件的话,则会使用 processComponent 函数进行处理,processComponent 函数的内容如下所示:

    1. const processComponent = (
    2. n1: VNode | null,
    3. n2: VNode,
    4. container: RendererElement,
    5. anchor: RendererNode | null,
    6. parentComponent: ComponentInternalInstance | null,
    7. parentSuspense: SuspenseBoundary | null,
    8. isSVG: boolean,
    9. slotScopeIds: string[] | null,
    10. optimized: boolean
    11. ) => {
    12. if (n1 == null) {
    13. mountComponent(
    14. n2,
    15. container,
    16. anchor,
    17. parentComponent,
    18. parentSuspense,
    19. isSVG,
    20. optimized
    21. )
    22. } else {
    23. updateComponent(n1, n2, optimized)
    24. }
    25. }

    在这里,判断 oldVNode 是否存在,如果存在的话,则执行 updateComponent 函数进行组件的更新,如果不存在的话,则执行 mountComponent 函数进行组件的挂载,我们首先看组件的挂载。

    3-1,组件的挂载

    1. // 挂载组件节点
    2. const mountComponent: MountComponentFn = (
    3. initialVNode,
    4. container,
    5. anchor,
    6. parentComponent,
    7. parentSuspense,
    8. isSVG,
    9. optimized
    10. ) => {
    11. // 创建组件实例对象
    12. const compatMountInstance =
    13. __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
    14. const instance: ComponentInternalInstance =
    15. compatMountInstance ||
    16. (initialVNode.component = createComponentInstance(
    17. initialVNode,
    18. parentComponent,
    19. parentSuspense
    20. ))
    21. // resolve props and slots for setup context
    22. // 解析初始化一些数据
    23. if (!(__COMPAT__ && compatMountInstance)) {
    24. setupComponent(instance)
    25. }
    26. setupRenderEffect(
    27. instance,
    28. initialVNode,
    29. container,
    30. anchor,
    31. parentSuspense,
    32. isSVG,
    33. optimized
    34. )
    35. }

    在 mountComponent 函数中,首先创建组件的实例,每渲染一次组件,就会创建一个对应的实例,组件实例就是一个对象,这个对象维护着组件运行过程中的所有信息,例如:注册的生命周期函数、组件上次渲染的 VNode,组件状态等等。一个组件实例的内容如下所示:

    1. const instance: ComponentInternalInstance = {
    2. uid: uid++,
    3. vnode,
    4. type,
    5. parent,
    6. appContext,
    7. root: null!, // to be immediately set
    8. next: null,
    9. subTree: null!, // will be set synchronously right after creation
    10. effect: null!,
    11. update: null!, // will be set synchronously right after creation
    12. scope: new EffectScope(true /* detached */),
    13. render: null,
    14. proxy: null,
    15. exposed: null,
    16. exposeProxy: null,
    17. withProxy: null,
    18. provides: parent ? parent.provides : Object.create(appContext.provides),
    19. accessCache: null!,
    20. renderCache: [],
    21. // local resolved assets
    22. components: null,
    23. directives: null,
    24. // resolved props and emits options
    25. propsOptions: normalizePropsOptions(type, appContext),
    26. emitsOptions: normalizeEmitsOptions(type, appContext),
    27. // emit
    28. emit: null!, // to be set immediately
    29. emitted: null,
    30. // props default value
    31. propsDefaults: EMPTY_OBJ,
    32. // inheritAttrs
    33. inheritAttrs: type.inheritAttrs,
    34. // state
    35. ctx: EMPTY_OBJ,
    36. data: EMPTY_OBJ,
    37. props: EMPTY_OBJ,
    38. attrs: EMPTY_OBJ,
    39. slots: EMPTY_OBJ,
    40. refs: EMPTY_OBJ,
    41. setupState: EMPTY_OBJ,
    42. setupContext: null,
    43. // suspense related
    44. suspense,
    45. suspenseId: suspense ? suspense.pendingId : 0,
    46. asyncDep: null,
    47. asyncResolved: false,
    48. // lifecycle hooks
    49. // not using enums here because it results in computed properties
    50. isMounted: false,
    51. isUnmounted: false,
    52. isDeactivated: false,
    53. bc: null,
    54. c: null,
    55. bm: null,
    56. m: null,
    57. bu: null,
    58. u: null,
    59. um: null,
    60. bum: null,
    61. da: null,
    62. a: null,
    63. rtg: null,
    64. rtc: null,
    65. ec: null,
    66. sp: null
    67. }

    上面的对象包含着很多的状态信息,是实现组件化一个很重要的内容。

    创建完组件实例后,Vue 使用 setupComponent 函数进行一些数据的解析和初始化,下面调用的 setupRenderEffect 函数是重点。

    1. const setupRenderEffect: SetupRenderEffectFn = (
    2. instance,
    3. initialVNode,
    4. container,
    5. anchor,
    6. parentSuspense,
    7. isSVG,
    8. optimized
    9. ) => {
    10. const componentUpdateFn = () => {
    11. // 使用组件实例的 isMounted 属性判断组件是否挂载
    12. // 如果为属性为 false,说明还未挂载,所以执行挂载逻辑
    13. // 如果属性为 true 的话,说明已经挂载,所以执行更新逻辑
    14. if (!instance.isMounted) {
    15. const { bm, m } = instance
    16. // beforeMount hook
    17. // 触发执行 beforeMount 生命周期函数
    18. if (bm) {
    19. invokeArrayFns(bm)
    20. }
    21. // 执行 render 函数,获取组件当前的 VNode
    22. const subTree = (instance.subTree = renderComponentRoot(instance))
    23. // 使用 patch 函数进行组件内容的渲染
    24. patch(
    25. null,
    26. subTree,
    27. container,
    28. anchor,
    29. instance,
    30. parentSuspense,
    31. isSVG
    32. )
    33. // mounted hook
    34. // 触发执行 mounted 生命周期函数
    35. if (m) {
    36. queuePostRenderEffect(m, parentSuspense)
    37. }
    38. // 将组件实例的 isMounted 属性设为 true,表明当前的组件已经完成了挂载操作
    39. instance.isMounted = true
    40. } else {
    41. let { bu, u } = instance
    42. // beforeUpdate hook
    43. // 触发执行 beforeUpdate 生命周期函数
    44. if (bu) {
    45. invokeArrayFns(bu)
    46. }
    47. // render
    48. // 执行 render 函数,获取组件最新的 VNode
    49. const nextTree = renderComponentRoot(instance)
    50. // 获取组件上次渲染的 VNode
    51. const prevTree = instance.subTree
    52. instance.subTree = nextTree
    53. // 使用 patch 函数进行组件的更新
    54. patch(
    55. prevTree,
    56. nextTree,
    57. // parent may have changed if it's in a teleport
    58. hostParentNode(prevTree.el!)!,
    59. // anchor may have changed if it's in a fragment
    60. getNextHostNode(prevTree),
    61. instance,
    62. parentSuspense,
    63. isSVG
    64. )
    65. // updated hook
    66. // 触发执行 updated 生命周期函数
    67. if (u) {
    68. queuePostRenderEffect(u, parentSuspense)
    69. }
    70. }
    71. }
    72. // 组件的更新借助了响应式系统中的 ReactiveEffect 类
    73. const effect = (instance.effect = new ReactiveEffect(
    74. componentUpdateFn,
    75. () => queueJob(update),
    76. instance.scope // track it in component's effect scope
    77. ))
    78. const update: SchedulerJob = (instance.update = () => effect.run())
    79. update()
    80. }

    上段代码中,借助 ReactiveEffect 类实现组件的更新,关于这个类的作用和源码可以看我的这篇博客,这里就不过多赘述了。

    这里实现功能的重点在 componentUpdateFn 函数中,在上面代码的最后,执行了 update 函数,这会进而触发执行上面 componentUpdateFn 函数的执行,componentUpdateFn 函数的内部会执行组件的 render 函数,render 函数会读取组件的响应式数据,这会触发依赖收集

    componentUpdateFn 函数的解析看上面的注释即可。

    3-2,组件的更新

    当后续 render 函数依赖的响应式数据发生变化的时候,会再次触发执行 componentUpdateFn 函数进行组件的重新渲染,详细解释看上面源码的注释。

    4,组件的 render 函数执行时,如何通过 this 访问到组件的响应式数据

    结论:Vue 通过代理让 render 函数执行时能够通过 this 访问到组件实例中的响应式数据。

    Vue 通过 renderComponentRoot 函数执行 render 函数,获取 VNode。

    instance.subTree = renderComponentRoot(instance)
    1. export function renderComponentRoot(
    2. instance: ComponentInternalInstance
    3. ): VNode {
    4. const {
    5. type: Component,
    6. vnode,
    7. proxy,
    8. props,
    9. propsOptions: [propsOptions],
    10. slots,
    11. attrs,
    12. emit,
    13. render,
    14. renderCache,
    15. data,
    16. setupState,
    17. ctx,
    18. inheritAttrs
    19. } = instance
    20. result = normalizeVNode(
    21. render!.call(
    22. proxy,
    23. proxy!,
    24. renderCache,
    25. props,
    26. setupState,
    27. data,
    28. ctx
    29. )
    30. )
    31. return result;
    32. }

    render 函数执行的时候,函数中的 this 指向 instance.proxy,接下来看 instance.proxy 属性是如何创建出来的。

    1. export function setupComponent(instance) {
    2. ......
    3. setupStatefulComponent(instance);
    4. }
    5. function setupStatefulComponent(instance) {
    6. instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);
    7. }
    8. export const PublicInstanceProxyHandlers = {
    9. get({ _: instance }: ComponentRenderContext, key: string) {
    10. const { ctx, setupState, data, props, accessCache, type, appContext } =
    11. instance
    12. if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
    13. accessCache![key] = AccessTypes.SETUP
    14. return setupState[key]
    15. } else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
    16. accessCache![key] = AccessTypes.DATA
    17. return data[key]
    18. } else if (
    19. (normalizedProps = instance.propsOptions[0]) &&
    20. hasOwn(normalizedProps, key)
    21. ) {
    22. accessCache![key] = AccessTypes.PROPS
    23. return props![key]
    24. } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {
    25. accessCache![key] = AccessTypes.CONTEXT
    26. return ctx[key]
    27. }
    28. }
    29. };

    可以发现,在 render 函数中通过 this 读取某些属性的时候,代理会判断 instance 的 setupState、data、props 中有没有同名的属性,如果有的话,就进行数据的读取和返回,并且这些被读取属性已经是响应式的了。
     

    5,setup 函数的实现

    setup 的官方文档点击这里

    setup 函数只会在组件挂载的时候执行一次,setup 函数既可以返回一个对象,也可以返回一个函数,如果返回的是一个对象的话,这个对象中的数据可以像 data 和 props 一样使用,如果返回的是一个函数的话,这个函数会被当成组件的 render 函数。

    源码如下所示:

    1. // 挂载组件节点
    2. const mountComponent: MountComponentFn = (
    3. initialVNode,
    4. container,
    5. anchor,
    6. parentComponent,
    7. parentSuspense,
    8. isSVG,
    9. optimized
    10. ) => {
    11. ......
    12. // 解析初始化一些数据
    13. if (!(__COMPAT__ && compatMountInstance)) {
    14. setupComponent(instance)
    15. }
    16. setupRenderEffect(
    17. instance,
    18. initialVNode,
    19. container,
    20. anchor,
    21. parentSuspense,
    22. isSVG,
    23. optimized
    24. )
    25. }
    26. export function setupComponent(instance) {
    27. ......
    28. setupStatefulComponent(instance);
    29. }
    30. function setupStatefulComponent(instance) {
    31. const Component = instance.type as ComponentOptions
    32. ......
    33. const { setup } = Component
    34. if (setup) {
    35. setCurrentInstance(instance)
    36. const setupResult = callWithErrorHandling(
    37. setup,
    38. instance,
    39. ErrorCodes.SETUP_FUNCTION,
    40. [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    41. )
    42. unsetCurrentInstance()
    43. handleSetupResult(instance, setupResult, isSSR)
    44. }
    45. }
    46. export function handleSetupResult(
    47. instance: ComponentInternalInstance,
    48. setupResult: unknown,
    49. isSSR: boolean
    50. ) {
    51. if (isFunction(setupResult)) {
    52. instance.render = setupResult as InternalRenderFunction
    53. } else if (isObject(setupResult)) {
    54. instance.setupState = proxyRefs(setupResult)
    55. }
    56. }

    在 setupStatefulComponent 函数中,获取用户编写的 setup 函数,执行它并获取 setup 函数的返回值 setupResult。

    接下来使用 handleSetupResult 函数处理结果,如果 setupResult 是一个函数的话,则将它赋值给组件实例的 render 属性,如果 setupResult 是一个对象的话,则将它赋值给组件实例的 setupState 属性上,当我们想在 render 函数中访问 setup 函数返回的数据时,Vue 会将读取操作代理到 setupState 属性上,源码如下所示:

    1. export const PublicInstanceProxyHandlers = {
    2. get({ _: instance }: ComponentRenderContext, key: string) {
    3. const { ctx, setupState, data, props, accessCache, type, appContext } =
    4. instance
    5. if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
    6. accessCache![key] = AccessTypes.SETUP
    7. return setupState[key]
    8. }
    9. ......
    10. }
    11. };

    6,组件生命周期的实现原理

    组件的生命周期原理很简单,主要分为两部分,分别是生命周期的注册以及生命周期的执行。

    首先说生命周期的注册,这里以 setup 函数中进行的生命周期注册为例。

    1. function setupStatefulComponent(instance) {
    2. const Component = instance.type as ComponentOptions
    3. ......
    4. const { setup } = Component
    5. if (setup) {
    6. setCurrentInstance(instance)
    7. const setupResult = callWithErrorHandling(
    8. setup,
    9. instance,
    10. ErrorCodes.SETUP_FUNCTION,
    11. [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    12. )
    13. unsetCurrentInstance()
    14. handleSetupResult(instance, setupResult, isSSR)
    15. }
    16. }
    17. export let currentInstance: ComponentInternalInstance | null = null
    18. export const setCurrentInstance = (instance: ComponentInternalInstance) => {
    19. currentInstance = instance
    20. }
    21. export const unsetCurrentInstance = () => {
    22. currentInstance = null
    23. }

    在生命周期函数执行前,会执行一个 setCurrentInstance 函数,这个函数的作用是将当前的组件实例设置到全局中。

    接下来看生命周期注册函数的内容,以 onMounted 函数为例:

    1. export const onMounted = createHook(LifecycleHooks.MOUNTED)
    2. export const createHook =
    3. extends Function = () => any>(lifecycle: LifecycleHooks) =>
    4. (hook: T, target: ComponentInternalInstance | null = currentInstance) =>
    5. // post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
    6. (!isInSSRComponentSetup || lifecycle === LifecycleHooks.SERVER_PREFETCH) &&
    7. injectHook(lifecycle, hook, target)
    8. export function injectHook(
    9. type: LifecycleHooks,
    10. hook: Function & { __weh?: Function },
    11. target: ComponentInternalInstance | null = currentInstance,
    12. prepend: boolean = false
    13. ): Function | undefined {
    14. if (target) {
    15. const hooks = target[type] || (target[type] = [])
    16. const wrappedHook =
    17. hook.__weh ||
    18. (hook.__weh = (...args: unknown[]) => {
    19. if (target.isUnmounted) {
    20. return
    21. }
    22. // disable tracking inside all lifecycle hooks
    23. // since they can potentially be called inside effects.
    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. setCurrentInstance(target)
    29. const res = callWithAsyncErrorHandling(hook, target, type, args)
    30. unsetCurrentInstance()
    31. resetTracking()
    32. return res
    33. })
    34. if (prepend) {
    35. hooks.unshift(wrappedHook)
    36. } else {
    37. hooks.push(wrappedHook)
    38. }
    39. return wrappedHook
    40. }
    41. }

    当我们在 setup 函数中执行 onMounted 等生命周期注册函数时,Vue 会将我们想要注册的生命周期函数保存到组件实例中,组件实例用于保存生命周期函数的属性如下所示:

    1. const instance: ComponentInternalInstance = {
    2. // lifecycle hooks
    3. bc: null,
    4. c: null,
    5. bm: null,
    6. m: null,
    7. bu: null,
    8. u: null,
    9. um: null,
    10. bum: null,
    11. da: null,
    12. a: null,
    13. rtg: null,
    14. rtc: null,
    15. ec: null,
    16. sp: null
    17. }

    知道了生命周期函数是如何注册的,接下来看看生命周期函数是如何触发的,生命周期函数触发的代码在 setupRenderEffect 函数中,代码如下所示:

    1. const setupRenderEffect: SetupRenderEffectFn = (
    2. instance,
    3. initialVNode,
    4. container,
    5. anchor,
    6. parentSuspense,
    7. isSVG,
    8. optimized
    9. ) => {
    10. const componentUpdateFn = () => {
    11. // 使用组件实例的 isMounted 属性判断组件是否挂载
    12. // 如果为属性为 false,说明还未挂载,所以执行挂载逻辑
    13. // 如果属性为 true 的话,说明已经挂载,所以执行更新逻辑
    14. if (!instance.isMounted) {
    15. const { bm, m } = instance
    16. // beforeMount hook
    17. // 触发执行 beforeMount 生命周期函数
    18. if (bm) {
    19. invokeArrayFns(bm)
    20. }
    21. // 执行 render 函数,获取组件当前的 VNode
    22. const subTree = (instance.subTree = renderComponentRoot(instance))
    23. // 使用 patch 函数进行组件内容的渲染
    24. patch(
    25. null,
    26. subTree,
    27. container,
    28. anchor,
    29. instance,
    30. parentSuspense,
    31. isSVG
    32. )
    33. // mounted hook
    34. // 触发执行 mounted 生命周期函数
    35. if (m) {
    36. queuePostRenderEffect(m, parentSuspense)
    37. }
    38. // 将组件实例的 isMounted 属性设为 true,表明当前的组件已经完成了挂载操作
    39. instance.isMounted = true
    40. } else {
    41. let { bu, u } = instance
    42. // beforeUpdate hook
    43. // 触发执行 beforeUpdate 生命周期函数
    44. if (bu) {
    45. invokeArrayFns(bu)
    46. }
    47. // render
    48. // 执行 render 函数,获取组件最新的 VNode
    49. const nextTree = renderComponentRoot(instance)
    50. // 获取组件上次渲染的 VNode
    51. const prevTree = instance.subTree
    52. instance.subTree = nextTree
    53. // 使用 patch 函数进行组件的更新
    54. patch(
    55. prevTree,
    56. nextTree,
    57. // parent may have changed if it's in a teleport
    58. hostParentNode(prevTree.el!)!,
    59. // anchor may have changed if it's in a fragment
    60. getNextHostNode(prevTree),
    61. instance,
    62. parentSuspense,
    63. isSVG
    64. )
    65. // updated hook
    66. // 触发执行 updated 生命周期函数
    67. if (u) {
    68. queuePostRenderEffect(u, parentSuspense)
    69. }
    70. }
    71. }
    72. // 组件的更新借助了响应式系统中的 ReactiveEffect 类
    73. const effect = (instance.effect = new ReactiveEffect(
    74. componentUpdateFn,
    75. () => queueJob(update),
    76. instance.scope // track it in component's effect scope
    77. ))
    78. const update: SchedulerJob = (instance.update = () => effect.run())
    79. update()
    80. }

    在 componentUpdateFn 函数中,进行了组件的初始挂载和更新,生命周期函数就是在这些操作的前后触发执行的,在上面的源码中,使用 invokeArrayFns 函数进行生命周期函数的触发执行,它的源码如下所示:

    1. export const invokeArrayFns = (fns: Function[], arg?: any) => {
    2. for (let i = 0; i < fns.length; i++) {
    3. fns[i](arg)
    4. }
    5. }

    7,结语

    这篇博客讲解了 Vue3 中是如何实现组件化的,下面的博客详细讲讲异步组件以及 Vue 提供的一些内置组件。

  • 相关阅读:
    Linux常用指令(十一)——关机重启
    10.10泊松、指数、伽马分布的理解
    数据结构——二叉树
    mySQL创建表的基础命令
    2023-10-11
    2个NPN三极管组成的恒流电路
    Linux进程
    java计算机毕业设计Web医学院校大学生就业信息管理系统源码+mysql数据库+系统+lw文档+部署1
    VsCode import自己写的包各种报错解决方案2022.9
    pytorch入门教程(小土堆
  • 原文地址:https://blog.csdn.net/f18855666661/article/details/126487122