Skip to content

Commit b3e07b4

Browse files
committed
feat: lifecycle hooks of dynamic sub-components
1 parent aa7e196 commit b3e07b4

File tree

4 files changed

+70
-14
lines changed

4 files changed

+70
-14
lines changed

Diff for: packages/runtime-vapor/__tests__/apiLifecycle.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ describe('api: lifecycle hooks', () => {
136136
it('onBeforeUnmount', async () => {
137137
const toggle = ref(true)
138138
const fn = vi.fn(() => {
139-
expect(host.innerHTML).toBe('<div></div>')
139+
expect(host.innerHTML).toBe('<div></div><!--if-->')
140140
})
141141
const { render, host } = define({
142142
setup() {
@@ -172,7 +172,7 @@ describe('api: lifecycle hooks', () => {
172172
it('onUnmounted', async () => {
173173
const toggle = ref(true)
174174
const fn = vi.fn(() => {
175-
expect(host.innerHTML).toBe('<div></div>')
175+
expect(host.innerHTML).toBe('<!--if-->')
176176
})
177177
const { render, host } = define({
178178
setup() {
@@ -208,7 +208,7 @@ describe('api: lifecycle hooks', () => {
208208
it('onBeforeUnmount in onMounted', async () => {
209209
const toggle = ref(true)
210210
const fn = vi.fn(() => {
211-
expect(host.innerHTML).toBe('<div></div>')
211+
expect(host.innerHTML).toBe('<div></div><!--if-->')
212212
})
213213
const { render, host } = define({
214214
setup() {

Diff for: packages/runtime-vapor/__tests__/component.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ describe('component', () => {
9696
'root beforeMount',
9797
'mid beforeMount',
9898
'child beforeMount',
99-
'child mounted',
100-
'mid mounted',
10199
'root mounted',
100+
'mid mounted',
101+
'child mounted',
102102
])
103103

104104
calls.length = 0
@@ -134,8 +134,8 @@ describe('component', () => {
134134
'root beforeUpdate',
135135
'mid beforeUnmount',
136136
'child beforeUnmount',
137-
'child unmounted',
138137
'mid unmounted',
138+
'child unmounted',
139139
'root updated',
140140
])
141141

@@ -150,8 +150,8 @@ describe('component', () => {
150150
'child created',
151151
'mid beforeMount',
152152
'child beforeMount',
153-
'child mounted',
154153
'mid mounted',
154+
'child mounted',
155155
'root updated',
156156
])
157157
})

Diff for: packages/runtime-vapor/src/apiCreateComponent.ts

+63-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
import {
22
type Component,
33
createComponentInstance,
4-
currentInstance,
4+
setCurrentInstance,
55
} from './component'
66
import { setupComponent } from './apiRender'
77
import type { RawProps } from './componentProps'
88
import type { RawSlots } from './componentSlots'
99
import { withAttrs } from './componentAttrs'
10+
import { getCurrentScope } from '@vue/reactivity'
11+
import type { BlockEffectScope } from './blockEffectScope'
12+
import {
13+
type Directive,
14+
type DirectiveHookName,
15+
invokeDirectiveHook,
16+
} from './directives'
17+
import { VaporLifecycleHooks } from './enums'
18+
import { NOOP, invokeArrayFns } from '@vue/shared'
1019

1120
export function createComponent(
1221
comp: Component,
@@ -15,7 +24,7 @@ export function createComponent(
1524
singleRoot: boolean = false,
1625
once: boolean = false,
1726
) {
18-
const current = currentInstance!
27+
const parentScope = getCurrentScope() as BlockEffectScope
1928
const instance = createComponentInstance(
2029
comp,
2130
singleRoot ? withAttrs(rawProps) : rawProps,
@@ -24,8 +33,58 @@ export function createComponent(
2433
)
2534
setupComponent(instance, singleRoot)
2635

27-
// register sub-component with current component for lifecycle management
28-
current.comps.add(instance)
36+
const directiveBindingsMap = (parentScope.dirs ||= new Map())
37+
const dir: Directive = {
38+
beforeMount: passDirectives(
39+
VaporLifecycleHooks.BEFORE_MOUNT,
40+
'beforeMount',
41+
),
42+
mounted: passDirectives(
43+
VaporLifecycleHooks.MOUNTED,
44+
'mounted',
45+
() => (instance.isMounted = true),
46+
true,
47+
),
48+
beforeUnmount: passDirectives(
49+
VaporLifecycleHooks.BEFORE_UNMOUNT,
50+
'beforeUnmount',
51+
),
52+
unmounted: passDirectives(
53+
VaporLifecycleHooks.UNMOUNTED,
54+
'unmounted',
55+
() => (instance.isUnmounted = true),
56+
true,
57+
),
58+
}
59+
directiveBindingsMap.set(instance, [
60+
{ dir, instance, value: null, oldValue: undefined },
61+
])
2962

3063
return instance
64+
65+
function passDirectives(
66+
lifecycle: VaporLifecycleHooks,
67+
directive: DirectiveHookName,
68+
cb = NOOP,
69+
reverse?: boolean,
70+
) {
71+
const hooks = reverse
72+
? [cb, callDirHooks, callLifecycleHooks]
73+
: [callLifecycleHooks, callDirHooks, cb]
74+
75+
return () => invokeArrayFns(hooks)
76+
77+
function callDirHooks() {
78+
invokeDirectiveHook(instance, directive, instance.scope)
79+
}
80+
function callLifecycleHooks() {
81+
// lifecycle hooks may be mounted halfway.
82+
const lifecycleHooks = instance[lifecycle]
83+
if (lifecycleHooks && lifecycleHooks.length) {
84+
const reset = setCurrentInstance(instance)
85+
instance.scope.run(() => invokeArrayFns(lifecycleHooks))
86+
reset()
87+
}
88+
}
89+
}
3190
}

Diff for: packages/runtime-vapor/src/apiLifecycle.ts

-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ const injectHook = (
2424
const wrappedHook =
2525
hook.__weh ||
2626
(hook.__weh = (...args: unknown[]) => {
27-
if (target.isUnmounted) {
28-
return
29-
}
3027
pauseTracking()
3128
const reset = setCurrentInstance(target)
3229
const res = target.scope.run(() =>

0 commit comments

Comments
 (0)