mirror of https://github.com/vuejs/core.git
feat(runtime-vapor): mounted & unmounted hook (#46)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
9dda97e736
commit
9d3abcf24b
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { type ComponentInternalInstance, currentInstance } from './component'
|
||||||
|
|
||||||
|
export enum VaporLifecycleHooks {
|
||||||
|
BEFORE_CREATE = 'bc',
|
||||||
|
CREATED = 'c',
|
||||||
|
BEFORE_MOUNT = 'bm',
|
||||||
|
MOUNTED = 'm',
|
||||||
|
BEFORE_UPDATE = 'bu',
|
||||||
|
UPDATED = 'u',
|
||||||
|
BEFORE_UNMOUNT = 'bum',
|
||||||
|
UNMOUNTED = 'um',
|
||||||
|
DEACTIVATED = 'da',
|
||||||
|
ACTIVATED = 'a',
|
||||||
|
RENDER_TRIGGERED = 'rtg',
|
||||||
|
RENDER_TRACKED = 'rtc',
|
||||||
|
ERROR_CAPTURED = 'ec',
|
||||||
|
// SERVER_PREFETCH = 'sp',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const injectHook = (
|
||||||
|
type: VaporLifecycleHooks,
|
||||||
|
hook: Function,
|
||||||
|
target: ComponentInternalInstance | null = currentInstance,
|
||||||
|
prepend: boolean = false,
|
||||||
|
) => {
|
||||||
|
if (target) {
|
||||||
|
const hooks = target[type] || (target[type] = [])
|
||||||
|
if (prepend) {
|
||||||
|
hooks.unshift(hook)
|
||||||
|
} else {
|
||||||
|
hooks.push(hook)
|
||||||
|
}
|
||||||
|
return hook
|
||||||
|
} else if (__DEV__) {
|
||||||
|
// TODO: warn need
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const createHook =
|
||||||
|
<T extends Function = () => any>(lifecycle: VaporLifecycleHooks) =>
|
||||||
|
(hook: T, target: ComponentInternalInstance | null = currentInstance) =>
|
||||||
|
injectHook(lifecycle, (...args: unknown[]) => hook(...args), target)
|
||||||
|
|
||||||
|
export const onBeforeMount = createHook(VaporLifecycleHooks.BEFORE_MOUNT)
|
||||||
|
export const onMounted = createHook(VaporLifecycleHooks.MOUNTED)
|
||||||
|
export const onBeforeUpdate = createHook(VaporLifecycleHooks.BEFORE_UPDATE)
|
||||||
|
export const onUpdated = createHook(VaporLifecycleHooks.UPDATED)
|
||||||
|
export const onBeforeUnmount = createHook(VaporLifecycleHooks.BEFORE_UNMOUNT)
|
||||||
|
export const onUnmounted = createHook(VaporLifecycleHooks.UNMOUNTED)
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
} from './componentProps'
|
} from './componentProps'
|
||||||
|
|
||||||
import type { Data } from '@vue/shared'
|
import type { Data } from '@vue/shared'
|
||||||
|
import { VaporLifecycleHooks } from './apiLifecycle'
|
||||||
|
|
||||||
export type Component = FunctionalComponent | ObjectComponent
|
export type Component = FunctionalComponent | ObjectComponent
|
||||||
|
|
||||||
|
|
@ -24,6 +25,8 @@ export interface ObjectComponent {
|
||||||
render(ctx: any): Block
|
render(ctx: any): Block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LifecycleHook<TFn = Function> = TFn[] | null
|
||||||
|
|
||||||
export interface ComponentInternalInstance {
|
export interface ComponentInternalInstance {
|
||||||
uid: number
|
uid: number
|
||||||
container: ParentNode
|
container: ParentNode
|
||||||
|
|
@ -44,8 +47,66 @@ export interface ComponentInternalInstance {
|
||||||
|
|
||||||
// lifecycle
|
// lifecycle
|
||||||
get isMounted(): boolean
|
get isMounted(): boolean
|
||||||
|
get isUnmounted(): boolean
|
||||||
|
isUnmountedRef: Ref<boolean>
|
||||||
isMountedRef: Ref<boolean>
|
isMountedRef: Ref<boolean>
|
||||||
// TODO: registory of provides, appContext, lifecycles, ...
|
// TODO: registory of provides, appContext, lifecycles, ...
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.BEFORE_CREATE]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.CREATED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.BEFORE_MOUNT]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.MOUNTED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.BEFORE_UPDATE]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.UPDATED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.UNMOUNTED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.RENDER_TRACKED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.RENDER_TRIGGERED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.ACTIVATED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.DEACTIVATED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.ERROR_CAPTURED]: LifecycleHook
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
// [VaporLifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise<unknown>>
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
@ -67,17 +128,17 @@ export const createComponentInstance = (
|
||||||
component: ObjectComponent | FunctionalComponent,
|
component: ObjectComponent | FunctionalComponent,
|
||||||
): ComponentInternalInstance => {
|
): ComponentInternalInstance => {
|
||||||
const isMountedRef = ref(false)
|
const isMountedRef = ref(false)
|
||||||
|
const isUnmountedRef = ref(false)
|
||||||
const instance: ComponentInternalInstance = {
|
const instance: ComponentInternalInstance = {
|
||||||
uid: uid++,
|
uid: uid++,
|
||||||
block: null,
|
block: null,
|
||||||
container: null!, // set on mount
|
container: null!, // set on mountComponent
|
||||||
scope: new EffectScope(true /* detached */)!,
|
scope: new EffectScope(true /* detached */)!,
|
||||||
component,
|
component,
|
||||||
|
|
||||||
// resolved props and emits options
|
// resolved props and emits options
|
||||||
propsOptions: normalizePropsOptions(component),
|
propsOptions: normalizePropsOptions(component),
|
||||||
// emitsOptions: normalizeEmitsOptions(type, appContext), // TODO:
|
// emitsOptions: normalizeEmitsOptions(type, appContext), // TODO:
|
||||||
|
|
||||||
proxy: null,
|
proxy: null,
|
||||||
|
|
||||||
// state
|
// state
|
||||||
|
|
@ -90,8 +151,68 @@ export const createComponentInstance = (
|
||||||
get isMounted() {
|
get isMounted() {
|
||||||
return isMountedRef.value
|
return isMountedRef.value
|
||||||
},
|
},
|
||||||
|
get isUnmounted() {
|
||||||
|
return isUnmountedRef.value
|
||||||
|
},
|
||||||
isMountedRef,
|
isMountedRef,
|
||||||
|
isUnmountedRef,
|
||||||
// TODO: registory of provides, appContext, lifecycles, ...
|
// TODO: registory of provides, appContext, lifecycles, ...
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.BEFORE_CREATE]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.CREATED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.BEFORE_MOUNT]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.MOUNTED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.BEFORE_UPDATE]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.UPDATED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.BEFORE_UNMOUNT]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.UNMOUNTED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.RENDER_TRACKED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.RENDER_TRIGGERED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.ACTIVATED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.DEACTIVATED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
[VaporLifecycleHooks.ERROR_CAPTURED]: null,
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
// [VaporLifecycleHooks.SERVER_PREFETCH]: null,
|
||||||
}
|
}
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,4 +44,5 @@ export * from './scheduler'
|
||||||
export * from './directive'
|
export * from './directive'
|
||||||
export * from './dom'
|
export * from './dom'
|
||||||
export * from './directives/vShow'
|
export * from './directives/vShow'
|
||||||
|
export * from './apiLifecycle'
|
||||||
export { getCurrentInstance, type ComponentInternalInstance } from './component'
|
export { getCurrentInstance, type ComponentInternalInstance } from './component'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { markRaw, proxyRefs } from '@vue/reactivity'
|
import { markRaw, proxyRefs } from '@vue/reactivity'
|
||||||
import { type Data } from '@vue/shared'
|
import { invokeArrayFns, type Data } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
type Component,
|
type Component,
|
||||||
type ComponentInternalInstance,
|
type ComponentInternalInstance,
|
||||||
|
|
@ -62,30 +62,37 @@ export function mountComponent(
|
||||||
}
|
}
|
||||||
return (instance.block = block)
|
return (instance.block = block)
|
||||||
})!
|
})!
|
||||||
|
const { bm, m } = instance
|
||||||
|
|
||||||
|
// hook: beforeMount
|
||||||
|
bm && invokeArrayFns(bm)
|
||||||
invokeDirectiveHook(instance, 'beforeMount')
|
invokeDirectiveHook(instance, 'beforeMount')
|
||||||
|
|
||||||
insert(block, instance.container)
|
insert(block, instance.container)
|
||||||
instance.isMountedRef.value = true
|
instance.isMountedRef.value = true
|
||||||
invokeDirectiveHook(instance, 'mounted')
|
|
||||||
unsetCurrentInstance()
|
|
||||||
|
|
||||||
// TODO: lifecycle hooks (mounted, ...)
|
// hook: mounted
|
||||||
// const { m } = instance
|
invokeDirectiveHook(instance, 'mounted')
|
||||||
// m && invoke(m)
|
m && invokeArrayFns(m)
|
||||||
|
unsetCurrentInstance()
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unmountComponent(instance: ComponentInternalInstance) {
|
export function unmountComponent(instance: ComponentInternalInstance) {
|
||||||
const { container, block, scope } = instance
|
const { container, block, scope, um, bum } = instance
|
||||||
|
|
||||||
|
// hook: beforeUnmount
|
||||||
|
bum && invokeArrayFns(bum)
|
||||||
invokeDirectiveHook(instance, 'beforeUnmount')
|
invokeDirectiveHook(instance, 'beforeUnmount')
|
||||||
|
|
||||||
scope.stop()
|
scope.stop()
|
||||||
block && remove(block, container)
|
block && remove(block, container)
|
||||||
instance.isMountedRef.value = false
|
instance.isMountedRef.value = false
|
||||||
invokeDirectiveHook(instance, 'unmounted')
|
instance.isUnmountedRef.value = true
|
||||||
unsetCurrentInstance()
|
|
||||||
|
|
||||||
// TODO: lifecycle hooks (unmounted, ...)
|
// hook: unmounted
|
||||||
// const { um } = instance
|
invokeDirectiveHook(instance, 'unmounted')
|
||||||
// um && invoke(um)
|
um && invokeArrayFns(um)
|
||||||
|
unsetCurrentInstance()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,31 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue/vapor'
|
import {
|
||||||
|
ref,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
onBeforeMount,
|
||||||
|
getCurrentInstance
|
||||||
|
} from 'vue/vapor'
|
||||||
|
|
||||||
|
const instance = getCurrentInstance()!
|
||||||
const count = ref(1)
|
const count = ref(1)
|
||||||
const double = computed(() => count.value * 2)
|
const double = computed(() => count.value * 2)
|
||||||
const html = computed(() => `<button>HTML! ${count.value}</button>`)
|
const html = computed(() => `<button>HTML! ${count.value}</button>`)
|
||||||
|
|
||||||
const inc = () => count.value++
|
const inc = () => count.value++
|
||||||
const dec = () => count.value--
|
const dec = () => count.value--
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
console.log('onBeforeMount', instance.isMounted)
|
||||||
|
})
|
||||||
|
onMounted(() => {
|
||||||
|
console.log('onMounted', instance.isMounted)
|
||||||
|
})
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
count.value++
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue