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的组件系统展现了现代前端框架的设计精髓:
- 灵活定义 - 多种组件定义方式
- 完整生命周期 - 丰富的生命周期钩子
- 高效渲染 - 智能的组件渲染机制
- 组件通信 - Props、Events、Inject/Provide
- 性能优化 - 异步组件、组件缓存
- 类型安全 - 完整的TypeScript支持
这套组件系统不仅为Vue3提供了强大的组件能力,也为其他前端项目提供了优秀的设计参考。