Skip to content

feat(runtime-vapor): onMounted and onUnMounted hook #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
093fbd9
feat(runtime-vapor): onMounted and onUnMounted hook
GaoNeng-wWw Dec 9, 2023
719601b
styles: Revision review comments
GaoNeng-wWw Dec 9, 2023
4bc650b
fix: UnMounted -> Unmounted
GaoNeng-wWw Dec 9, 2023
c301020
fix(runtime-vapor): name conflict for enum LifecycleHooks error
GaoNeng-wWw Dec 9, 2023
26308c5
feat: camel modifier for `v-bind` (#39)
LittleSound Dec 9, 2023
0521be9
fix(runtime-vapor): set isUnMountedRef to true when unmount component
GaoNeng-wWw Dec 9, 2023
341ddf0
feat(compiler-vapor/v-bind): globally allowed
sxzz Dec 9, 2023
45e86e3
fix(compiler-vapor): generate static expression
sxzz Dec 9, 2023
da8e196
refactor(compiler-vapor): v-on
sxzz Dec 9, 2023
4b4cb05
refactor: pushMulti
sxzz Dec 9, 2023
0c26b0d
feat: withIndent
sxzz Dec 9, 2023
ecf7da9
feat: pushFnCall
sxzz Dec 9, 2023
12250a8
feat(runtime-vapor): component props (#40)
ubugeeei Dec 9, 2023
b421aa9
test: combine with transform and codegen tests for `v-bind` (#45)
LittleSound Dec 9, 2023
d1dd1e1
fix(compiler-vapor): add modifier for empty v-on
sxzz Dec 9, 2023
6852ade
feat(runtime-vapor): onMounted and onUnMounted hook
GaoNeng-wWw Dec 9, 2023
98d48e7
styles: Revision review comments
GaoNeng-wWw Dec 9, 2023
4855284
fix: UnMounted -> Unmounted
GaoNeng-wWw Dec 9, 2023
cef4d28
fix(runtime-vapor): name conflict for enum LifecycleHooks error
GaoNeng-wWw Dec 9, 2023
25bdc24
fix(runtime-vapor): set isUnMountedRef to true when unmount component
GaoNeng-wWw Dec 9, 2023
bc72922
fix: Conflict Resolution
GaoNeng-wWw Dec 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions packages/runtime-vapor/src/apiLifecycle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { pauseTracking, resetTracking } from '@vue/reactivity'
import {
ComponentInternalInstance,
currentInstance,
setCurrentInstance,
} from './component'

export 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',
RENDER_TRIGGERED = 'rtg',
RENDER_TRACKED = 'rtc',
ERROR_CAPTURED = 'ec',
SERVER_PREFETCH = 'sp',
}
export const injectHook = (
type: LifecycleHooks,
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 =
<T extends Function = () => any>(lifecycle: LifecycleHooks) =>
(hook: T, target: ComponentInternalInstance | null = currentInstance) =>
injectHook(lifecycle, (...args: unknown[]) => hook(...args), target)

export const onMounted = createHook(LifecycleHooks.MOUNTED)
export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
export const onUpdated = createHook(LifecycleHooks.UPDATED)
export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
124 changes: 122 additions & 2 deletions packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type Ref, EffectScope, ref } from '@vue/reactivity'
import type { Block } from './render'
import type { DirectiveBinding } from './directive'
import type { Data } from '@vue/shared'
import { LifecycleHooks } from './apiLifecycle'

export type SetupFn = (props: any, ctx: any) => Block | Data
export type FunctionalComponent = SetupFn & {
Expand All @@ -11,7 +12,7 @@ export interface ObjectComponent {
setup: SetupFn
render(ctx: any): Block
}

type LifecycleHook<TFn = Function> = TFn[] | null
export interface ComponentInternalInstance {
uid: number
container: ParentNode
Expand All @@ -20,11 +21,69 @@ export interface ComponentInternalInstance {

component: FunctionalComponent | ObjectComponent
get isMounted(): boolean
get isUnMounted(): boolean
isMountedRef: Ref<boolean>
isUnMountedRef: Ref<boolean>
Copy link
Member

@ubugeeei ubugeeei Dec 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand the handling of this flag. @sxzz

At the very least, if implementing 'isUnmounted', it would probably be good to rewrite it as true during unmount.


ref: #42

Flags related to lifecycle
I had implemented it so that isMounted is set to false when unmounting,
In traditional components, it seems that isUnmounted is set to true.
Which approach should we follow for the implementation? (In this PR, I have added the isUnmounted flag.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value of 'isUnmounted' in 'runtime-core' is false, so I have set it to false.

Copy link
Member

@ubugeeei ubugeeei Dec 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that seems to be the correct default value,
but I do think it should be marked as true when the Component is unmounted.
https://github.com/vuejs/core-vapor/pull/43/files#diff-e05b05171d6196eb88c0845a17e0482ab342a992e8e388da3dca363fadeafc08R67-R80

(However, currently when unmounting, the implementation sets isMounted to false, so I'm not quite clear on how these two flags should be treated...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I forgot. My bad. 😢

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no, please don't feel bad. 😸

In any case, since I don't have a clear grasp of how these two flags should be treated, I'd like to leave the rest to @sxzz ...


/** directives */
dirs: Map<Node, DirectiveBinding[]>
// TODO: registory of provides, appContext, lifecycles, ...
/**
* @internal
*/
[LifecycleHooks.BEFORE_CREATE]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.CREATED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.BEFORE_MOUNT]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.MOUNTED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.BEFORE_UPDATE]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.UPDATED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.UNMOUNTED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.RENDER_TRACKED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.RENDER_TRIGGERED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.ACTIVATED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.DEACTIVATED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.ERROR_CAPTURED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise<unknown>>
}

// TODO
Expand All @@ -46,20 +105,81 @@ 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,
get isMounted() {
return isMountedRef.value
},
get isUnMounted() {
return isUnMountedRef.value
},
isMountedRef,
isUnMountedRef,

dirs: new Map(),
// TODO: registory of provides, appContext, lifecycles, ...
/**
* @internal
*/
[LifecycleHooks.BEFORE_CREATE]: null,
/**
* @internal
*/
[LifecycleHooks.CREATED]: null,
/**
* @internal
*/
[LifecycleHooks.BEFORE_MOUNT]: null,
/**
* @internal
*/
[LifecycleHooks.MOUNTED]: null,
/**
* @internal
*/
[LifecycleHooks.BEFORE_UPDATE]: null,
/**
* @internal
*/
[LifecycleHooks.UPDATED]: null,
/**
* @internal
*/
[LifecycleHooks.BEFORE_UNMOUNT]: null,
/**
* @internal
*/
[LifecycleHooks.UNMOUNTED]: null,
/**
* @internal
*/
[LifecycleHooks.RENDER_TRACKED]: null,
/**
* @internal
*/
[LifecycleHooks.RENDER_TRIGGERED]: null,
/**
* @internal
*/
[LifecycleHooks.ACTIVATED]: null,
/**
* @internal
*/
[LifecycleHooks.DEACTIVATED]: null,
/**
* @internal
*/
[LifecycleHooks.ERROR_CAPTURED]: null,
/**
* @internal
*/
[LifecycleHooks.SERVER_PREFETCH]: null,
}
return instance
}
1 change: 1 addition & 0 deletions packages/runtime-vapor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ export * from './scheduler'
export * from './directive'
export * from './dom'
export * from './directives/vShow'
export * from './apiLifecycle'
12 changes: 9 additions & 3 deletions packages/runtime-vapor/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from './component'
import { invokeDirectiveHook } from './directive'
import { insert, remove } from './dom'
import { invokeArrayFns } from '@vue/shared'

export type Block = Node | Fragment | Block[]
export type ParentBlock = ParentNode | Node[]
Expand All @@ -30,7 +31,6 @@ export function normalizeContainer(container: string | ParentNode): ParentNode {
? (document.querySelector(container) as ParentNode)
: container
}

export function mountComponent(
instance: ComponentInternalInstance,
container: ParentNode,
Expand Down Expand Up @@ -58,7 +58,10 @@ export function mountComponent(
insert(block, instance.container)
instance.isMountedRef.value = true
invokeDirectiveHook(instance, 'mounted')

const { m } = instance
if (m) {
invokeArrayFns(m)
}
// TODO: lifecycle hooks (mounted, ...)
// const { m } = instance
// m && invoke(m)
Expand All @@ -73,7 +76,10 @@ export function unmountComponent(instance: ComponentInternalInstance) {
instance.isMountedRef.value = false
invokeDirectiveHook(instance, 'unmounted')
unsetCurrentInstance()

const { um } = instance
if (um) {
invokeArrayFns(um)
}
// TODO: lifecycle hooks (unmounted, ...)
// const { um } = instance
// um && invoke(um)
Expand Down