我的主页我的主页
主页
教程
Vue3源码解析
主页
教程
Vue3源码解析
  • Vue3核心知识

    • Vue3核心知识点总览
    • Vue3架构设计详解
    • Vue3响应式系统详解
    • Vue3虚拟DOM系统详解
    • Vue3编译器系统详解
    • Vue3组件系统详解
    • Vue3性能优化详解

Vue3组件系统详解

🎯 组件系统概述

Vue3的组件系统是框架的核心抽象层,它提供了组件定义、注册、渲染和生命周期管理的完整解决方案。

组件系统架构图

┌─────────────────────────────────────┐
│        组件定义层 (Definition)      │
│  defineComponent() | 类组件 | 函数组件│
├─────────────────────────────────────┤
│        组件注册层 (Registration)    │
│  app.component() | 全局 | 局部      │
├─────────────────────────────────────┤
│        组件渲染层 (Rendering)       │
│  mount() | patch() | update()       │
├─────────────────────────────────────┤
│        生命周期层 (Lifecycle)       │
│  setup() | mounted() | unmounted()  │
└─────────────────────────────────────┘

🏗️ 组件定义

1. 组件类型

// 组件类型定义
export type Component =
  | ComponentOptions
  | FunctionalComponent
  | ComponentPublicInstanceConstructor

// 组件选项
export interface ComponentOptions {
  name?: string
  components?: Record<string, Component>
  directives?: Record<string, Directive>
  inheritAttrs?: boolean
  emits?: string[] | Record<string, any>
  setup?: (
    props: Readonly<LooseRequired<Props>>,
    ctx: SetupContext,
  ) => RenderFunction | object | void
  template?: string
  render?: RenderFunction
  // ... 更多选项
}

// 函数式组件
export type FunctionalComponent<Props = {}, Emits = {}, Slots = {}> = {
  (props: Props, ctx: SetupContext<Emits, Slots>): VNodeChild
  displayName?: string
  inheritAttrs?: boolean
  props?: ComponentPropsOptions<Props>
  emits?: ComponentEmitsOptions<Emits>
  slots?: SlotsType<Slots>
}

2. defineComponent

// defineComponent函数
export function defineComponent<T extends ComponentOptions>(options: T): T

export function defineComponent<T extends ComponentOptions, RawBindings>(
  options: T & ThisType<CreateComponentPublicInstance<T, RawBindings>>,
): T

export function defineComponent<T extends ComponentOptions>(
  setup: T['setup'],
  extraOptions?: Omit<T, 'setup'>,
): T

// 使用示例
const MyComponent = defineComponent({
  name: 'MyComponent',
  props: {
    message: String,
    count: {
      type: Number,
      default: 0,
    },
  },
  emits: ['update'],
  setup(props, { emit }) {
    const increment = () => {
      emit('update', props.count + 1)
    }

    return {
      increment,
    }
  },
  template: `
    <div>
      <p>{{ message }}</p>
      <p>Count: {{ count }}</p>
      <button @click="increment">Increment</button>
    </div>
  `,
})

🔧 组件实例

1. 组件内部实例

// 组件内部实例接口
export interface ComponentInternalInstance {
  uid: number
  type: ConcreteComponent
  parent: ComponentInternalInstance | null
  root: ComponentInternalInstance
  appContext: AppContext
  vnode: VNode
  next: VNode | null
  subTree: VNode
  update: ReactiveEffect
  scope: EffectScope
  render: InternalRenderFunction
  proxy: ComponentPublicInstance | null
  exposed: Record<string, any> | null
  exposeProxy: Record<string, any> | null
  withProxy: ComponentPublicInstance | null
  provides: Data
  accessCache: Data | null
  renderCache: (VNode | undefined)[]
  components: Record<string, Component>
  directives: Record<string, Directive>
  filters: Record<string, Function> | undefined
  propsOptions: NormalizedPropsOptions
  emitsOptions: NormalizedEmitsOptions | null
  emit: EmitFn | null
  emitted: Record<string, any> | null
  attrs: Data
  slots: InternalSlots
  refs: Data
  setupState: Data
  setupContext: SetupContext | null
  ctx: Data
  data: Data
  props: Data
  attrs: Data
  slots: InternalSlots
  refs: Data
  parent: ComponentInternalInstance | null
  appContext: AppContext
  emit: EmitFn
  update: ReactiveEffect
  isUnmounted: boolean
  isDeactivated: boolean
  [LifecycleHooks.BEFORE_CREATE]: LifecycleHook[]
  [LifecycleHooks.CREATED]: LifecycleHook[]
  [LifecycleHooks.BEFORE_MOUNT]: LifecycleHook[]
  [LifecycleHooks.MOUNTED]: LifecycleHook[]
  [LifecycleHooks.BEFORE_UPDATE]: LifecycleHook[]
  [LifecycleHooks.UPDATED]: LifecycleHook[]
  [LifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook[]
  [LifecycleHooks.UNMOUNTED]: LifecycleHook[]
  [LifecycleHooks.ACTIVATED]: LifecycleHook[]
  [LifecycleHooks.DEACTIVATED]: LifecycleHook[]
  [LifecycleHooks.ERROR_CAPTURED]: LifecycleHook[]
  [LifecycleHooks.RENDER_TRACKED]: LifecycleHook[]
  [LifecycleHooks.RENDER_TRIGGERED]: LifecycleHook[]
  [LifecycleHooks.SERVER_PREFETCH]: LifecycleHook[]
}

2. 组件公共实例

// 组件公共实例接口
export interface ComponentPublicInstance<
  P = {},
  B = {},
  D = {},
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  E extends EmitsOptions = {},
  PublicProps = P,
  Defaults = {},
  MakeDefaultsOptional extends boolean = false,
  Options = ComponentOptions,
> {
  $: ComponentInternalInstance
  $data: D
  $props: MakeDefaultsOptional extends true ? Partial<Defaults> & P : P
  $attrs: Data
  $refs: Data
  $slots: Slots
  $root: ComponentPublicInstance | null
  $parent: ComponentPublicInstance | null
  $emit: EmitFn<E>
  $el: any
  $options: Options & MergedComponentOptionsOverride
  $forceUpdate(): void
  $nextTick(fn?: () => void): Promise<void>
  $watch(
    source: string | Function,
    cb: Function,
    options?: WatchOptions,
  ): WatchStopHandle
}

🎛️ 组件渲染

1. 组件渲染流程

// 组件渲染函数
function renderComponentRoot(instance: ComponentInternalInstance): VNode {
  const {
    type: Component,
    vnode,
    proxy,
    withProxy,
    props,
    slots,
    attrs,
    emit,
    render,
    renderCache,
    data,
    setupState,
    ctx,
    inheritAttrs,
  } = instance

  let result: VNode
  let fallthroughAttrs: string[] | undefined

  try {
    if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
      // 有状态组件
      const proxyToUse = withProxy || proxy
      result = normalizeVNode(
        render!.call(
          proxyToUse,
          proxyToUse!,
          renderCache,
          props,
          setupState,
          data,
          ctx,
        ),
      )
      fallthroughAttrs = inheritAttrs !== false ? attrs : undefined
    } else {
      // 函数式组件
      const render = Component as FunctionalComponent
      result = normalizeVNode(
        render.length > 1
          ? render(props, { attrs, slots, emit })
          : render(props, null as any),
      )
      fallthroughAttrs = attrs
    }
  } catch (err) {
    handleError(err, instance, ErrorCodes.RENDER_FUNCTION)
    result = createVNode(Comment)
  }

  // 处理fallthrough属性
  if (fallthroughAttrs && Object.keys(fallthroughAttrs).length) {
    if (result.shapeFlag & ShapeFlags.ELEMENT) {
      result.props = { ...result.props, ...fallthroughAttrs }
    } else if (result.shapeFlag & ShapeFlags.COMPONENT) {
      if (result.props) {
        result.props = { ...result.props, ...fallthroughAttrs }
      } else {
        result.props = fallthroughAttrs
      }
    }
  }

  return result
}

2. 组件挂载

// 组件挂载流程
function mountComponent(
  vnode: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  namespace: ElementNamespace,
  slotScopeIds: string[] | null,
  optimized: boolean,
) {
  const instance: ComponentInternalInstance = (vnode.component =
    createComponentInstance(vnode, parentComponent, parentSuspense))

  if (instance.type.__hmrId) {
    registerHMR(instance)
  }

  // 注入插件数据
  if (instance.type.inheritAttrs !== false) {
    inheritAttrs(instance, vnode)
  }

  // 设置组件实例
  if (instance.type.setup) {
    const setupResult = callSetup(instance)
    if (isFunction(setupResult)) {
      instance.render = setupResult as InternalRenderFunction
    } else if (isObject(setupResult)) {
      instance.setupState = proxyRefs(setupResult)
    }
  }

  if (instance.render) {
    // 创建渲染effect
    instance.update = effect(
      function componentEffect() {
        if (!instance.isMounted) {
          // 首次挂载
          mountComponent(instance, container, anchor)
        } else {
          // 更新
          updateComponent(instance)
        }
      },
      __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions,
    )
  }
}

🔄 生命周期

1. 生命周期钩子

// 生命周期钩子枚举
export enum LifecycleHooks {
  BEFORE_CREATE = 'bc',
  CREATED = 'c',
  BEFORE_MOUNT = 'bm',
  MOUNTED = 'm',
  BEFORE_UPDATE = 'bu',
  UPDATED = 'u',
  BEFORE_UNMOUNT = 'bum',
  UNMOUNTED = 'um',
  ACTIVATED = 'a',
  DEACTIVATED = 'da',
  ERROR_CAPTURED = 'ec',
  RENDER_TRACKED = 'rtc',
  RENDER_TRIGGERED = 'rtg',
  SERVER_PREFETCH = 'sp',
}

// 生命周期钩子类型
export type LifecycleHook = Function[] | null

// 注册生命周期钩子
export function onBeforeMount(
  hook: Function,
  target?: ComponentInternalInstance | null,
) {
  if (!target) {
    target = currentInstance
  }
  if (target) {
    addHook(LifecycleHooks.BEFORE_MOUNT, hook, target)
  }
}

export function onMounted(
  hook: Function,
  target?: ComponentInternalInstance | null,
) {
  if (!target) {
    target = currentInstance
  }
  if (target) {
    addHook(LifecycleHooks.MOUNTED, hook, target)
  }
}

// 添加钩子
function addHook(
  type: LifecycleHooks,
  hook: Function,
  target: ComponentInternalInstance | null = currentInstance,
) {
  if (target) {
    const hooks = target[type] || (target[type] = [])
    const wrappedHook = hook.__weh || hook
    hooks.push(wrappedHook)
  }
}

2. 生命周期执行

// 执行生命周期钩子
function callHook(
  hook: LifecycleHook,
  instance: ComponentInternalInstance,
  type: LifecycleHooks,
) {
  if (hook) {
    const args = type === LifecycleHooks.BEFORE_CREATE ? [instance.proxy] : []
    callWithAsyncErrorHandling(hook, instance, type, args)
  }
}

// 组件创建
function createComponentInstance(
  vnode: VNode,
  parent: ComponentInternalInstance | null,
  suspense: SuspenseBoundary | null,
): ComponentInternalInstance {
  const type = vnode.type as ConcreteComponent
  const appContext =
    (parent ? parent.appContext : vnode.appContext) || emptyAppContext

  const instance: ComponentInternalInstance = {
    uid: uid++,
    vnode,
    type,
    parent,
    appContext,
    root: null!, // to be immediately set
    next: null,
    subTree: null!, // will be set synchronously right after creation
    update: null!, // will be set synchronously right after creation
    scope: new EffectScope(true /* detached */),
    render: null,
    proxy: null,
    exposed: null,
    exposeProxy: null,
    withProxy: null,
    provides: parent ? parent.provides : Object.create(appContext.provides),
    accessCache: null!,
    renderCache: [],
    components: null,
    directives: null,
    filters: undefined,
    propsOptions: normalizePropsOptions(type, appContext),
    emitsOptions: normalizeEmitsOptions(type, appContext),
    emit: null!, // to be set immediately
    emitted: null,
    attrs: EMPTY_OBJ,
    slots: EMPTY_OBJ,
    refs: EMPTY_OBJ,
    setupState: EMPTY_OBJ,
    setupContext: null,
    ctx: EMPTY_OBJ,
    data: EMPTY_OBJ,
    props: EMPTY_OBJ,
    attrs: EMPTY_OBJ,
    slots: EMPTY_OBJ,
    refs: EMPTY_OBJ,
    parent: parent,
    appContext: appContext,
    emit: null!,
    update: null!,
    isUnmounted: false,
    isDeactivated: false,
    [LifecycleHooks.BEFORE_CREATE]: null,
    [LifecycleHooks.CREATED]: null,
    [LifecycleHooks.BEFORE_MOUNT]: null,
    [LifecycleHooks.MOUNTED]: null,
    [LifecycleHooks.BEFORE_UPDATE]: null,
    [LifecycleHooks.UPDATED]: null,
    [LifecycleHooks.BEFORE_UNMOUNT]: null,
    [LifecycleHooks.UNMOUNTED]: null,
    [LifecycleHooks.ACTIVATED]: null,
    [LifecycleHooks.DEACTIVATED]: null,
    [LifecycleHooks.ERROR_CAPTURED]: null,
    [LifecycleHooks.RENDER_TRACKED]: null,
    [LifecycleHooks.RENDER_TRIGGERED]: null,
    [LifecycleHooks.SERVER_PREFETCH]: null,
  }

  // 设置根实例
  instance.root = parent ? parent.root : instance

  // 设置emit函数
  instance.emit = emit.bind(null, instance)

  return instance
}

🎯 组件通信

1. Props传递

// Props规范化
function normalizePropsOptions(
  comp: ConcreteComponent,
  appContext: AppContext,
  asMixin = false,
): NormalizedPropsOptions {
  const cache = appContext.propsCache
  const cached = cache.get(comp)
  if (cached) {
    return cached
  }

  const raw = comp.props || {}
  const normalized: NormalizedPropsOptions = {}
  const needCastKeys: NormalizedPropsOptions = {}

  // 处理数组形式的props
  if (isArray(raw)) {
    for (let i = 0; i < raw.length; i++) {
      const key = camelize(raw[i])
      normalized[key] = EMPTY_OBJ
    }
  } else if (raw) {
    for (const key in raw) {
      const normalizedKey = camelize(key)
      const opt = raw[key]
      const prop: NormalizedProp = (normalized[normalizedKey] =
        isArray(opt) || isFunction(opt) ? { type: opt } : opt)
      if (prop) {
        const booleanIndex = getTypeIndex(Boolean, prop.type)
        const stringIndex = getTypeIndex(String, prop.type)
        prop[BooleanFlags.shouldCast] = booleanIndex > stringIndex
        prop[BooleanFlags.shouldCastTrue] =
          stringIndex < 0 || booleanIndex < stringIndex
        if (booleanIndex >= 0 || hasOwn(prop, 'default')) {
          needCastKeys[normalizedKey] = true
        }
      }
    }
  }

  const res: NormalizedPropsOptions = [normalized, needCastKeys]
  cache.set(comp, res)
  return res
}

2. 事件发射

// 事件发射函数
export function emit(
  instance: ComponentInternalInstance,
  event: string,
  ...args: any[]
) {
  const props = instance.vnode.props || EMPTY_OBJ

  let handler = props[`on${event[0].toUpperCase() + event.slice(1)}`]
  if (!handler && event.startsWith('update:')) {
    handler = props[`on${event[2].toUpperCase() + event.slice(3)}`]
  }
  if (handler) {
    callWithAsyncErrorHandling(
      handler,
      instance,
      ErrorCodes.COMPONENT_EVENT_HANDLER,
      args,
    )
  }

  const onceHandler = props[`on${event[0].toUpperCase() + event.slice(1)}Once`]
  if (onceHandler) {
    if (!instance.emitted) {
      instance.emitted = {}
    } else if (instance.emitted[event]) {
      return
    }
    instance.emitted[event] = true
    callWithAsyncErrorHandling(
      onceHandler,
      instance,
      ErrorCodes.COMPONENT_EVENT_HANDLER,
      args,
    )
  }
}

3. 依赖注入

// 提供依赖
export function provide<T>(key: InjectionKey<T> | string | number, value: T) {
  if (!currentInstance) {
    if (__DEV__) {
      warn(`provide() can only be used inside setup().`)
    }
  } else {
    let provides = currentInstance.provides
    const parentProvides =
      currentInstance.parent && currentInstance.parent.provides
    if (parentProvides === provides) {
      provides = currentInstance.provides = Object.create(parentProvides)
    }
    provides[key as string] = value
  }
}

// 注入依赖
export function inject<T>(
  key: InjectionKey<T> | string,
  defaultValue?: T,
  treatDefaultAsFactory = false,
): T | undefined {
  if (!currentInstance) {
    if (__DEV__) {
      warn(`inject() can only be used inside setup().`)
    }
  } else {
    const provides = currentInstance.parent?.provides
    if (provides && (key as string | symbol) in provides) {
      return provides[key as string]
    } else if (arguments.length > 1) {
      return treatDefaultAsFactory && isFunction(defaultValue)
        ? defaultValue.call(currentInstance.proxy)
        : defaultValue
    } else if (__DEV__) {
      warn(`injection "${String(key)}" not found.`)
    }
  }
}

📊 组件优化

1. 异步组件

// 异步组件定义
export function defineAsyncComponent<
  T extends Component = { new (): ComponentPublicInstance },
>(source: AsyncComponentLoader<T> | AsyncComponentOptions<T>): T {
  if (isFunction(source)) {
    source = { loader: source }
  }

  const {
    loader,
    loadingComponent,
    errorComponent,
    delay = 200,
    timeout,
    suspensible = true,
    onError: userOnError,
  } = source

  let pendingRequest: Promise<ConcreteComponent> | null = null
  let resolvedComp: ConcreteComponent | undefined

  let retries = 0
  const retry = () => {
    retries++
    pendingRequest = null
    return load()
  }

  const load = (): Promise<ConcreteComponent> => {
    let thisRequest: Promise<ConcreteComponent>
    return loader()
      .then(comp => {
        if (thisRequest !== pendingRequest && pendingRequest !== null) {
          return thisRequest
        }
        if (__DEV__ && !comp) {
          throw new Error(
            `Async component loader resolved to undefined. If you are using retry(), make sure to return a new promise.`,
          )
        }
        if (resolvedComp && resolvedComp !== comp) {
          return resolvedComp
        }
        resolvedComp = comp
        if (comp && (comp as any).__vccOpts) {
          comp = (comp as any).__vccOpts
        }
        return comp
      })
      .catch(err => {
        err = err instanceof Error ? err : new Error(String(err))
        if (__DEV__) {
          warn(`Async component loader failed:`, err)
        }
        if (userOnError) {
          return new Promise((resolve, reject) => {
            const userRetry = () => resolve(retry())
            const userFail = () => reject(err)
            userOnError(err, userRetry, userFail, retries + 1)
          })
        } else {
          throw err
        }
      })
  }

  return defineComponent({
    name: 'AsyncComponentWrapper',
    __asyncLoader: load,
    get __asyncResolved() {
      return resolvedComp
    },
    setup() {
      const instance = currentInstance!

      if (resolvedComp) {
        return () => h(resolvedComp)
      }

      const onError = (err: Error) => {
        pendingRequest = null
        handleError(err, instance, ErrorCodes.ASYNC_COMPONENT_LOADER)
      }

      let loaded = false
      let error = ref()
      let delayed = ref(!!delay)

      if (delay) {
        setTimeout(() => {
          delayed.value = false
        }, delay)
      }

      if (timeout != null) {
        setTimeout(() => {
          if (!loaded.value && !error.value) {
            const err = new Error(
              `Async component timed out after ${timeout}ms.`,
            )
            onError(err)
            error.value = err
          }
        }, timeout)
      }

      load()
        .then(comp => {
          loaded.value = true
          resolvedComp = comp
          if (instance.parent && isKeepAlive(instance.parent.vnode)) {
            ;(instance.parent.ctx as KeepAliveContext).deactivate(instance)
          }
        })
        .catch(err => {
          onError(err)
          error.value = err
        })

      return () => {
        if (loaded.value && resolvedComp) {
          return h(resolvedComp)
        } else if (error.value && errorComponent) {
          return h(errorComponent, { error: error.value })
        } else if (loadingComponent && !delayed.value) {
          return h(loadingComponent)
        }
      }
    },
  }) as T
}

2. 组件缓存

// KeepAlive组件
export const KeepAliveImpl = {
  name: `KeepAlive`,
  __isKeepAlive: true,
  inheritRef: true,
  props: {
    include: [String, RegExp, Array],
    exclude: [String, RegExp, Array],
    max: [String, Number],
  },
  setup(props: KeepAliveProps, { slots }: SetupContext) {
    const instance = getCurrentInstance()!
    const sharedContext = instance.ctx as KeepAliveContext

    if (!sharedContext.renderer) {
      return () => {
        const children = slots.default && slots.default()
        if (!children) {
          return null
        }
        const child = children[0]
        if (children.length > 1) {
          if (__DEV__) {
            warn(`KeepAlive should contain exactly one component child.`)
          }
          return children
        } else if (
          !isVNode(child) ||
          !(child.shapeFlag & ShapeFlags.STATEFUL_COMPONENT)
        ) {
          return child
        }
        return child
      }
    }

    const cache = new Map<Component, VNode>()
    const keys = new Set<string>()
    let current: VNode | null = null

    const {
      renderer: {
        p: patch,
        m: move,
        um: unmount,
        o: { createElement },
      },
    } = sharedContext

    const storageContainer = createElement('div')

    sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
      const instance = vnode.component!
      move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
      patch(
        instance.vnode,
        vnode,
        container,
        anchor,
        instance,
        parentSuspense,
        isSVG,
        vnode.slotScopeIds,
        optimized,
      )
      queuePostRenderEffect(() => {
        instance.isDeactivated = false
        if (instance.a) {
          invokeArrayFns(instance.a)
        }
        const vnodeHook = vnode.props && vnode.props.onVnodeMounted
        if (vnodeHook) {
          invokeVNodeHook(vnodeHook, instance.parent, vnode)
        }
      }, parentSuspense)
    }

    sharedContext.deactivate = (vnode: VNode) => {
      const instance = vnode.component!
      move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
      queuePostRenderEffect(() => {
        if (instance.da) {
          invokeArrayFns(instance.da)
        }
        const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted
        if (vnodeHook) {
          invokeVNodeHook(vnodeHook, instance.parent, vnode)
        }
        instance.isDeactivated = true
      }, parentSuspense)
    }

    function unmount(vnode: VNode) {
      resetShapeFlag(vnode)
      unmount(vnode, instance, parentSuspense, true)
    }

    function pruneCache(filter?: (name: string) => boolean) {
      cache.forEach((vnode, key) => {
        const name = getComponentName(vnode.type as ConcreteComponent)
        if (name && (!filter || !filter(name))) {
          pruneCacheEntry(key)
        }
      })
    }

    function pruneCacheEntry(key: CacheKey) {
      const cached = cache.get(key) as VNode
      if (!current || cached.type !== current.type) {
        unmount(cached)
      } else if (current) {
        resetShapeFlag(current)
      }
      cache.delete(key)
      keys.delete(key)
    }

    watch(
      () => [props.include, props.exclude],
      ([include, exclude]) => {
        include && pruneCache(name => matches(include, name))
        exclude && pruneCache(name => !matches(exclude, name))
      },
      { flush: 'post', deep: true },
    )

    let pendingCacheKey: CacheKey | null = null
    const cacheSubtree = () => {
      if (pendingCacheKey != null) {
        cache.set(pendingCacheKey, getInnerChild(instance.subTree))
      }
    }
    onMounted(cacheSubtree)
    onUpdated(cacheSubtree)
    onBeforeUnmount(() => {
      cache.forEach(cached => {
        const { subTree, suspense } = instance
        const currentSubTree = getInnerChild(subTree)
        if (cached.type === currentSubTree.type) {
          resetShapeFlag(currentSubTree)
          const da = currentSubTree.component!.da
          if (da) {
            invokeArrayFns(da)
          }
        } else {
          unmount(cached)
        }
      })
    })

    return () => {
      pendingCacheKey = null
      if (!slots.default) {
        return null
      }

      const children = slots.default()
      const rawVNode = children[0]
      if (children.length > 1) {
        if (__DEV__) {
          warn(`KeepAlive should contain exactly one component child.`)
        }
        current = null
        return children
      } else if (
        !isVNode(rawVNode) ||
        !(rawVNode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT)
      ) {
        current = null
        return rawVNode
      }

      let vnode = getInnerChild(rawVNode)
      const comp = vnode.type as ConcreteComponent
      const name = getComponentName(
        isAsyncWrapper(vnode) ? vnode.type.__asyncResolved || {} : comp,
      )

      const { include, exclude, max } = props

      if (
        (include && (!name || !matches(include, name))) ||
        (exclude && name && matches(exclude, name))
      ) {
        current = vnode
        return rawVNode
      }

      const key = vnode.key == null ? comp : vnode.key
      const cachedVNode = cache.get(key)

      if (vnode.el) {
        vnode = cloneVNode(vnode)
        if (rawVNode.shapeFlag & ShapeFlags.COMPONENT_STYLED) {
          rawVNode.shapeFlag &= ~ShapeFlags.COMPONENT_STYLED
        }
      }
      pendingCacheKey = key

      if (cachedVNode) {
        vnode.el = cachedVNode.el
        vnode.component = cachedVNode.component
        if (vnode.transition) {
          setTransitionHooks(vnode, vnode.transition!)
        }
        vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
        keys.delete(key)
        keys.add(key)
      } else {
        keys.add(key)
        if (max && keys.size > parseInt(max as string, 10)) {
          pruneCacheEntry(keys.values().next().value)
        }
      }
      vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE

      current = vnode
      return isSuspense(rawVNode) ? rawVNode : vnode
    }
  },
}

🎯 总结

Vue3的组件系统展现了现代前端框架的设计精髓:

  1. 灵活定义 - 多种组件定义方式
  2. 完整生命周期 - 丰富的生命周期钩子
  3. 高效渲染 - 智能的组件渲染机制
  4. 组件通信 - Props、Events、Inject/Provide
  5. 性能优化 - 异步组件、组件缓存
  6. 类型安全 - 完整的TypeScript支持

这套组件系统不仅为Vue3提供了强大的组件能力,也为其他前端项目提供了优秀的设计参考。

最近更新:: 2025/8/7 11:48
Prev
Vue3编译器系统详解
Next
Vue3性能优化详解