From 4215840f8571796337deac5a8f96343bc6b5067e Mon Sep 17 00:00:00 2001 From: GaoNeng-wWw Date: Sun, 10 Dec 2023 12:42:52 +0800 Subject: [PATCH 1/6] feat(runtime-vapor): mounted & unmounted hook --- packages/runtime-vapor/src/apiLifecycle.ts | 66 +++++++++++ packages/runtime-vapor/src/component.ts | 125 ++++++++++++++++++++- packages/runtime-vapor/src/render.ts | 13 +-- 3 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 packages/runtime-vapor/src/apiLifecycle.ts diff --git a/packages/runtime-vapor/src/apiLifecycle.ts b/packages/runtime-vapor/src/apiLifecycle.ts new file mode 100644 index 000000000..732884ace --- /dev/null +++ b/packages/runtime-vapor/src/apiLifecycle.ts @@ -0,0 +1,66 @@ +import { pauseTracking, resetTracking } from '@vue/reactivity' +import { + type ComponentInternalInstance, + currentInstance, + setCurrentInstance, +} 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 & { __weh?: Function }, + target: ComponentInternalInstance | null = currentInstance, + prepend: boolean = false, +) => { + if (target) { + const hooks = target[type] || (target[type] = []) + const wrappedHook = + hook.__weh || + (hook.__weh = (...args: unknown[]) => { + if (target.isUnmounted) { + return + } + pauseTracking() + setCurrentInstance(target) + // TODO: call error handling + const res = hook(...args) + resetTracking() + return res + }) + if (prepend) { + hooks.unshift(wrappedHook) + } else { + hooks.push(wrappedHook) + } + return wrappedHook + } else if (__DEV__) { + // TODO: warn need + } +} +export const createHook = + any>(lifecycle: VaporLifecycleHooks) => + (hook: T, target: ComponentInternalInstance | null = currentInstance) => + injectHook(lifecycle, (...args: unknown[]) => hook(...args), target) + +export const onMounted = createHook(VaporLifecycleHooks.MOUNTED) +export const onBeforeMount = createHook(VaporLifecycleHooks.BEFORE_MOUNT) +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) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 68e3c6942..5db71d559 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -10,6 +10,7 @@ import { } from './componentProps' import type { Data } from '@vue/shared' +import { VaporLifecycleHooks } from './apiLifecycle' export type Component = FunctionalComponent | ObjectComponent @@ -24,6 +25,8 @@ export interface ObjectComponent { render(ctx: any): Block } +type LifecycleHook = TFn[] | null + export interface ComponentInternalInstance { uid: number container: ParentNode @@ -44,8 +47,66 @@ export interface ComponentInternalInstance { // lifecycle get isMounted(): boolean + get isUnmounted(): boolean + isUnmountedRef: Ref isMountedRef: Ref // 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> } // TODO @@ -67,17 +128,17 @@ export const createComponentInstance = ( component: ObjectComponent | FunctionalComponent, ): ComponentInternalInstance => { const isMountedRef = ref(false) + const isUnmountedRef = ref(false) const instance: ComponentInternalInstance = { uid: uid++, block: null, - container: null!, // set on mount + container: null!, scope: new EffectScope(true /* detached */)!, component, // resolved props and emits options propsOptions: normalizePropsOptions(component), // emitsOptions: normalizeEmitsOptions(type, appContext), // TODO: - proxy: null, // state @@ -90,8 +151,68 @@ export const createComponentInstance = ( get isMounted() { return isMountedRef.value }, + get isUnmounted() { + return isUnmountedRef.value + }, isMountedRef, + isUnmountedRef, // 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 } diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index 123c9f5d8..b16c02e60 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -1,5 +1,5 @@ import { markRaw, proxyRefs } from '@vue/reactivity' -import { type Data } from '@vue/shared' +import { invokeArrayFns, type Data } from '@vue/shared' import { type Component, type ComponentInternalInstance, @@ -68,9 +68,8 @@ export function mountComponent( invokeDirectiveHook(instance, 'mounted') unsetCurrentInstance() - // TODO: lifecycle hooks (mounted, ...) - // const { m } = instance - // m && invoke(m) + const { m } = instance + m && invokeArrayFns(m) return instance } @@ -85,7 +84,7 @@ export function unmountComponent(instance: ComponentInternalInstance) { invokeDirectiveHook(instance, 'unmounted') unsetCurrentInstance() - // TODO: lifecycle hooks (unmounted, ...) - // const { um } = instance - // um && invoke(um) + const { um } = instance + um && invokeArrayFns(um) + instance.isUnmountedRef.value = true } From ca9477c8da8d8ad93a8ef26a14a61637cbf9ba42 Mon Sep 17 00:00:00 2001 From: GaoNeng-wWw Date: Sun, 10 Dec 2023 12:58:33 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=90=9E=20fix(runtime-vapor):=20export?= =?UTF-8?q?=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/runtime-vapor/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index 3a88a738c..7f6165677 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -44,4 +44,5 @@ export * from './scheduler' export * from './directive' export * from './dom' export * from './directives/vShow' +export * from './apiLifecycle' export { getCurrentInstance, type ComponentInternalInstance } from './component' From d6d363b99c937af127f5b7866e98be7e84e47b8c Mon Sep 17 00:00:00 2001 From: GaoNeng-wWw Date: Wed, 13 Dec 2023 10:25:19 +0800 Subject: [PATCH 3/6] feat(vapor-runtime): beforeUnmounted && beforeMounted Hook --- packages/runtime-vapor/src/render.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index b16c02e60..96f447d2e 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -62,21 +62,22 @@ export function mountComponent( } return (instance.block = block) })! + const { bm, m } = instance + + bm && invokeArrayFns(bm) invokeDirectiveHook(instance, 'beforeMount') insert(block, instance.container) instance.isMountedRef.value = true invokeDirectiveHook(instance, 'mounted') unsetCurrentInstance() - - const { m } = instance m && invokeArrayFns(m) - return instance } export function unmountComponent(instance: ComponentInternalInstance) { - const { container, block, scope } = instance + const { container, block, scope, um, bum } = instance + bum && invokeArrayFns(bum) invokeDirectiveHook(instance, 'beforeUnmount') scope.stop() block && remove(block, container) @@ -84,7 +85,6 @@ export function unmountComponent(instance: ComponentInternalInstance) { invokeDirectiveHook(instance, 'unmounted') unsetCurrentInstance() - const { um } = instance um && invokeArrayFns(um) instance.isUnmountedRef.value = true } From 15b775aaf541540359add91997e1c6787be8be0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Fri, 15 Dec 2023 01:41:32 +0800 Subject: [PATCH 4/6] refactor --- packages/runtime-vapor/src/apiLifecycle.ts | 32 +++++----------------- packages/runtime-vapor/src/component.ts | 4 +-- packages/runtime-vapor/src/render.ts | 16 ++++++++--- 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/packages/runtime-vapor/src/apiLifecycle.ts b/packages/runtime-vapor/src/apiLifecycle.ts index 732884ace..eff675728 100644 --- a/packages/runtime-vapor/src/apiLifecycle.ts +++ b/packages/runtime-vapor/src/apiLifecycle.ts @@ -1,9 +1,4 @@ -import { pauseTracking, resetTracking } from '@vue/reactivity' -import { - type ComponentInternalInstance, - currentInstance, - setCurrentInstance, -} from './component' +import { type ComponentInternalInstance, currentInstance } from './component' export enum VaporLifecycleHooks { BEFORE_CREATE = 'bc', @@ -19,36 +14,23 @@ export enum VaporLifecycleHooks { RENDER_TRIGGERED = 'rtg', RENDER_TRACKED = 'rtc', ERROR_CAPTURED = 'ec', - SERVER_PREFETCH = 'sp', + // SERVER_PREFETCH = 'sp', } export const injectHook = ( type: VaporLifecycleHooks, - hook: Function & { __weh?: Function }, + hook: Function, target: ComponentInternalInstance | null = currentInstance, prepend: boolean = false, ) => { if (target) { const hooks = target[type] || (target[type] = []) - const wrappedHook = - hook.__weh || - (hook.__weh = (...args: unknown[]) => { - if (target.isUnmounted) { - return - } - pauseTracking() - setCurrentInstance(target) - // TODO: call error handling - const res = hook(...args) - resetTracking() - return res - }) if (prepend) { - hooks.unshift(wrappedHook) + hooks.unshift(hook) } else { - hooks.push(wrappedHook) + hooks.push(hook) } - return wrappedHook + return hook } else if (__DEV__) { // TODO: warn need } @@ -58,8 +40,8 @@ export const createHook = (hook: T, target: ComponentInternalInstance | null = currentInstance) => injectHook(lifecycle, (...args: unknown[]) => hook(...args), target) -export const onMounted = createHook(VaporLifecycleHooks.MOUNTED) 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) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 5db71d559..5caf44175 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -106,7 +106,7 @@ export interface ComponentInternalInstance { /** * @internal */ - [VaporLifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise> + // [VaporLifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise> } // TODO @@ -212,7 +212,7 @@ export const createComponentInstance = ( /** * @internal */ - [VaporLifecycleHooks.SERVER_PREFETCH]: null, + // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } return instance } diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index 96f447d2e..a2f505ecd 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -64,27 +64,35 @@ export function mountComponent( })! const { bm, m } = instance + // hook: beforeMount bm && invokeArrayFns(bm) invokeDirectiveHook(instance, 'beforeMount') + insert(block, instance.container) instance.isMountedRef.value = true + + // hook: mounted invokeDirectiveHook(instance, 'mounted') - unsetCurrentInstance() m && invokeArrayFns(m) + unsetCurrentInstance() + return instance } export function unmountComponent(instance: ComponentInternalInstance) { const { container, block, scope, um, bum } = instance + // hook: beforeUnmount bum && invokeArrayFns(bum) invokeDirectiveHook(instance, 'beforeUnmount') + scope.stop() block && remove(block, container) instance.isMountedRef.value = false - invokeDirectiveHook(instance, 'unmounted') - unsetCurrentInstance() + instance.isUnmountedRef.value = true + // hook: unmounted + invokeDirectiveHook(instance, 'unmounted') um && invokeArrayFns(um) - instance.isUnmountedRef.value = true + unsetCurrentInstance() } From f252388811b62a1a98d72dddfbd2f8cb25dada8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Fri, 15 Dec 2023 01:43:51 +0800 Subject: [PATCH 5/6] revert --- packages/runtime-vapor/src/component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 5caf44175..23b5f0770 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -132,7 +132,7 @@ export const createComponentInstance = ( const instance: ComponentInternalInstance = { uid: uid++, block: null, - container: null!, + container: null!, // set on mountComponent scope: new EffectScope(true /* detached */)!, component, From c891bed7ec0442d13885b7836ce11f558034bebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Fri, 15 Dec 2023 01:47:09 +0800 Subject: [PATCH 6/6] chore: add playground --- playground/src/App.vue | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/playground/src/App.vue b/playground/src/App.vue index eb83971a3..7e0abdccc 100644 --- a/playground/src/App.vue +++ b/playground/src/App.vue @@ -1,12 +1,31 @@