The registration mechanism of lifecycle hooks
Lifecycle Hook Registration Mechanism
Vue3's lifecycle hook registration mechanism is designed based on the Composition API, with core logic concentrated in the packages/runtime-core/src/component.ts
file. Unlike Vue2's Options API, Vue3 implements hook registration by calling lifecycle APIs inside the setup()
function. This mechanism enables more precise type inference and better logic reuse.
Hook Function Type Definitions
Vue3 internally uses the LifecycleHooks
enum to define all supported hook types, with each hook corresponding to an array storing callback functions:
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',
ERROR_CAPTURED = 'ec',
RENDER_TRACKED = 'rtc',
RENDER_TRIGGERED = 'rtg'
}
Each component instance maintains a hooks
object, whose properties correspond to the above enum values, with values being arrays of callback functions:
interface ComponentInternalInstance {
hooks: Record<string, Function[]>
}
Implementation Principle of Registration Process
When calling APIs like onMounted
, the actual execution is handled by the injectHook
function:
export function injectHook(
type: LifecycleHooks,
hook: Function,
target: ComponentInternalInstance | null = currentInstance
) {
if (target) {
const hooks = target[type] || (target[type] = [])
const wrappedHook = hook.__weh || hook
hooks.push(wrappedHook)
return wrappedHook
}
}
Typical lifecycle API implementations are as follows:
export const onMounted = (hook: Function) =>
injectHook(LifecycleHooks.MOUNTED, hook)
Asynchronous Dependency Handling Mechanism
Vue3 adds special logic for handling lifecycle hooks in async components. When a Suspense
context is detected, some lifecycle hooks are deferred:
export function onMounted(hook: Function) {
if (getCurrentInstance()) {
injectHook('m', hook)
} else if (__FEATURE_SUSPENSE__ && isInSuspense) {
queueLifecycleHook(hook)
} else {
warn(`onMounted is called when there is no active component instance.`)
}
}
Hook Execution Order Control
The execution order of lifecycle hooks is guaranteed by the internally defined invokeArrayFns
function:
export const invokeArrayFns = (fns: Function[], arg?: any) => {
for (let i = 0; i < fns.length; i++) {
fns[i](arg)
}
}
During the component mounting phase, hooks are triggered in the following order:
const setupRenderEffect = () => {
// Trigger beforeMount
invokeArrayFns(instance.hooks[LifecycleHooks.BEFORE_MOUNT])
// Execute rendering
const subTree = renderComponentRoot(instance)
// Trigger mounted
queuePostRenderEffect(() => {
invokeArrayFns(instance.hooks[LifecycleHooks.MOUNTED])
})
}
Special Handling for Server-Side Rendering
For SSR scenarios, some client-specific lifecycles are automatically skipped. This is distinguished internally via the __SSR__
flag:
export function onMounted(hook: Function) {
if (__SSR__ && !isInSuspense) {
return
}
injectHook(LifecycleHooks.MOUNTED, hook)
}
Error Handling Mechanism
The error capture lifecycle onErrorCaptured
has independent processing logic, maintaining a dedicated error handler queue:
export function callWithErrorHandling(
fn: Function,
instance: ComponentInternalInstance | null,
type: ErrorTypes,
args?: unknown[]
) {
try {
return args ? fn(...args) : fn()
} catch (err) {
if (instance) {
const errorInfo = __DEV__ ? ErrorTypeStrings[type] : type
triggerError(instance, err, errorInfo)
}
throw err
}
}
Interaction with KeepAlive Component
Activation/deactivation lifecycles are deeply integrated with the <KeepAlive>
component, triggered within cache control logic:
export function onActivated(hook: Function) {
registerKeepAliveHook(hook, LifecycleHooks.ACTIVATED)
}
export function onDeactivated(hook: Function) {
registerKeepAliveHook(hook, LifecycleHooks.DEACTIVATED)
}
Additional Checks in Development Environment
In development mode, extra validations are added for lifecycle usage:
export function validateLifecycleHook(
hook: Function,
instance: ComponentInternalInstance
) {
if (__DEV__ && hook && hook.length > 0) {
warn(
`Lifecycle hook "${hook.name}" has ${hook.length} parameters, ` +
`but the callback only accepts a single argument.`
)
}
}
Performance Optimization Measures
Vue3 optimizes high-frequency update lifecycles (e.g., beforeUpdate
):
export function queueLifecycleHook(hook: Function) {
if (__DEV__ && !currentInstance) {
warn(`Lifecycle hook "${hook.name}" is called outside of setup().`)
return
}
if (isHmrUpdating) {
return
}
nextTickPreFlushCbs.push(hook)
}
Integration with Scheduling System
The execution timing of lifecycle hooks is tightly coupled with Vue's scheduling system:
const flushPostFlushCbs = () => {
for (let i = 0; i < postFlushCbs.length; i++) {
postFlushCbs[i]()
}
}
Type System Support
TypeScript type definitions ensure type safety for lifecycle hooks:
declare type LifecycleHook<T extends Function = Function> = T & {
__weh?: T
}
export declare interface ComponentCustomOptions {
beforeCreate?(this: ComponentPublicInstance): void
created?(this: ComponentPublicInstance): void
// ...other hook types
}
Compatibility Handling Between Hooks and Options API
For cases using both Options API and Composition API, Vue3 merges the processing:
export function applyOptions(instance: ComponentInternalInstance) {
const options = instance.type
// Process beforeCreate
if (options.beforeCreate) {
callHook(options.beforeCreate, instance, LifecycleHooks.BEFORE_CREATE)
}
// Process hooks registered via Composition API
if (instance.hooks) {
Object.keys(instance.hooks).forEach(hook => {
registerLifecycleHook(instance, hook as LifecycleHooks)
})
}
}
Interaction Between Lifecycles and Reactive System
Some lifecycles trigger specific operations in the reactive system:
export function triggerBeforeUpdate() {
if (instance.hooks[LifecycleHooks.BEFORE_UPDATE]) {
pauseTracking()
invokeArrayFns(instance.hooks[LifecycleHooks.BEFORE_UPDATE])
resetTracking()
}
}
Custom Renderer Support
Custom renderers can extend or modify lifecycle behavior:
export function createRenderer(options: RendererOptions) {
const {
patchProp,
createElement,
// ...other methods
} = options
function mountComponent() {
// Custom mounting logic
callHook(instance, LifecycleHooks.BEFORE_MOUNT)
// ...
}
}
Coordination with Teleport Component
The Teleport component specially handles certain lifecycles:
export function resolveTarget(props: TeleportProps) {
if (isMounted) {
callHook(instance, LifecycleHooks.BEFORE_UPDATE)
}
// ...resolve target location
}
Lifecycles and VNode Association
Some lifecycles access VNode-related information:
export function renderComponentRoot(instance: ComponentInternalInstance): VNode {
callHook(instance, LifecycleHooks.BEFORE_RENDER)
const result = instance.render.call(proxy)
callHook(instance, LifecycleHooks.RENDERED)
return result
}
DevTools Integration
DevTools listens to lifecycle events for debugging:
export function emitLifecycleHook(
instance: ComponentInternalInstance,
hook: LifecycleHooks
) {
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
devtools.emitLifecycle(instance, hook)
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:setup函数的执行时机