Skip to content

Commit 64e8368

Browse files
authored
refactor(runtime-vapor): renderEffect based on ReactiveEffect + remove renderWatch (#155)
1 parent 4676188 commit 64e8368

File tree

8 files changed

+86
-170
lines changed

8 files changed

+86
-170
lines changed

packages/runtime-vapor/__tests__/renderWatch.spec.ts renamed to packages/runtime-vapor/__tests__/renderEffect.spec.ts

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1+
import { onEffectCleanup } from '@vue/reactivity'
12
import {
23
nextTick,
34
onBeforeUpdate,
45
onUpdated,
5-
onWatcherCleanup,
66
ref,
77
renderEffect,
8-
renderWatch,
98
template,
109
watchEffect,
1110
watchPostEffect,
@@ -31,8 +30,8 @@ const createDemo = (setupFn: () => any, renderFn: (ctx: any) => any) =>
3130
},
3231
})
3332

34-
describe('renderWatch', () => {
35-
test('effect', async () => {
33+
describe('renderEffect', () => {
34+
test('basic', async () => {
3635
let dummy: any
3736
const source = ref(0)
3837
renderEffect(() => {
@@ -58,26 +57,6 @@ describe('renderWatch', () => {
5857
expect(dummy).toBe(3)
5958
})
6059

61-
test('watch', async () => {
62-
let dummy: any
63-
const source = ref(0)
64-
renderWatch(source, () => {
65-
dummy = source.value
66-
})
67-
await nextTick()
68-
expect(dummy).toBe(undefined)
69-
70-
source.value++
71-
expect(dummy).toBe(undefined)
72-
await nextTick()
73-
expect(dummy).toBe(1)
74-
75-
source.value++
76-
expect(dummy).toBe(1)
77-
await nextTick()
78-
expect(dummy).toBe(2)
79-
})
80-
8160
test('should run with the scheduling order', async () => {
8261
const calls: string[] = []
8362

@@ -101,17 +80,17 @@ describe('renderWatch', () => {
10180
watchPostEffect(() => {
10281
const current = source.value
10382
calls.push(`post ${current}`)
104-
onWatcherCleanup(() => calls.push(`post cleanup ${current}`))
83+
onEffectCleanup(() => calls.push(`post cleanup ${current}`))
10584
})
10685
watchEffect(() => {
10786
const current = source.value
10887
calls.push(`pre ${current}`)
109-
onWatcherCleanup(() => calls.push(`pre cleanup ${current}`))
88+
onEffectCleanup(() => calls.push(`pre cleanup ${current}`))
11089
})
11190
watchSyncEffect(() => {
11291
const current = source.value
11392
calls.push(`sync ${current}`)
114-
onWatcherCleanup(() => calls.push(`sync cleanup ${current}`))
93+
onEffectCleanup(() => calls.push(`sync cleanup ${current}`))
11594
})
11695
return { source, change, renderSource, changeRender }
11796
},
@@ -121,15 +100,8 @@ describe('renderWatch', () => {
121100
renderEffect(() => {
122101
const current = _ctx.renderSource
123102
calls.push(`renderEffect ${current}`)
124-
onWatcherCleanup(() => calls.push(`renderEffect cleanup ${current}`))
103+
onEffectCleanup(() => calls.push(`renderEffect cleanup ${current}`))
125104
})
126-
renderWatch(
127-
() => _ctx.renderSource,
128-
value => {
129-
calls.push(`renderWatch ${value}`)
130-
onWatcherCleanup(() => calls.push(`renderWatch cleanup ${value}`))
131-
},
132-
)
133105
},
134106
).render()
135107
const { change, changeRender } = instance.setupState as any
@@ -151,7 +123,6 @@ describe('renderWatch', () => {
151123
'beforeUpdate 1',
152124
'renderEffect cleanup 0',
153125
'renderEffect 1',
154-
'renderWatch 1',
155126
'post cleanup 0',
156127
'post 1',
157128
'updated 1',
@@ -172,8 +143,6 @@ describe('renderWatch', () => {
172143
'beforeUpdate 2',
173144
'renderEffect cleanup 1',
174145
'renderEffect 2',
175-
'renderWatch cleanup 1',
176-
'renderWatch 2',
177146
'post cleanup 1',
178147
'post 2',
179148
'updated 2',

packages/runtime-vapor/src/apiCreateFor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { type EffectScope, effectScope, isReactive } from '@vue/reactivity'
22
import { isArray, isObject, isString } from '@vue/shared'
33
import { createComment, createTextNode, insert, remove } from './dom/element'
4-
import { renderEffect } from './renderWatch'
4+
import { renderEffect } from './renderEffect'
55
import { type Block, type Fragment, fragmentKey } from './apiRender'
66
import { warn } from './warning'
77

packages/runtime-vapor/src/apiCreateIf.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { renderWatch } from './renderWatch'
1+
import { renderEffect } from './renderEffect'
22
import { type Block, type Fragment, fragmentKey } from './apiRender'
33
import { type EffectScope, effectScope } from '@vue/reactivity'
44
import { createComment, createTextNode, insert, remove } from './dom/element'
@@ -12,6 +12,8 @@ export const createIf = (
1212
b2?: BlockFn,
1313
// hydrationNode?: Node,
1414
): Fragment => {
15+
let newValue: any
16+
let oldValue: any
1517
let branch: BlockFn | undefined
1618
let parent: ParentNode | undefined | null
1719
let block: Block | undefined
@@ -29,25 +31,23 @@ export const createIf = (
2931
// setCurrentHydrationNode(hydrationNode!)
3032
// }
3133

32-
renderWatch(
33-
() => !!condition(),
34-
value => {
34+
renderEffect(() => {
35+
if ((newValue = !!condition()) !== oldValue) {
3536
parent ||= anchor.parentNode
3637
if (block) {
3738
scope!.stop()
3839
remove(block, parent!)
3940
}
40-
if ((branch = value ? b1 : b2)) {
41+
if ((branch = (oldValue = newValue) ? b1 : b2)) {
4142
scope = effectScope()
4243
fragment.nodes = block = scope.run(branch)!
4344
parent && insert(block, parent, anchor)
4445
} else {
4546
scope = block = undefined
4647
fragment.nodes = []
4748
}
48-
},
49-
{ immediate: true },
50-
)
49+
}
50+
})
5151

5252
// TODO: SSR
5353
// if (isHydrating) {

packages/runtime-vapor/src/directives.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { NOOP, isFunction } from '@vue/shared'
1+
import { isFunction } from '@vue/shared'
22
import { type ComponentInternalInstance, currentInstance } from './component'
3-
import { pauseTracking, resetTracking } from '@vue/reactivity'
3+
import { pauseTracking, resetTracking, traverse } from '@vue/reactivity'
44
import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
5-
import { renderWatch } from './renderWatch'
5+
import { renderEffect } from './renderEffect'
66

77
export type DirectiveModifiers<M extends string = string> = Record<M, boolean>
88

@@ -100,8 +100,12 @@ export function withDirectives<T extends Node>(
100100

101101
// register source
102102
if (source) {
103-
// callback will be overridden by middleware
104-
renderWatch(source, NOOP, { deep: dir.deep })
103+
if (dir.deep) {
104+
const deep = dir.deep === true ? undefined : dir.deep
105+
const baseSource = source
106+
source = () => traverse(baseSource(), deep)
107+
}
108+
renderEffect(source)
105109
}
106110
}
107111

packages/runtime-vapor/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export {
4646
type FunctionalComponent,
4747
type SetupFn,
4848
} from './component'
49-
export { renderEffect, renderWatch } from './renderWatch'
49+
export { renderEffect } from './renderEffect'
5050
export {
5151
watch,
5252
watchEffect,
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { EffectFlags, ReactiveEffect, type SchedulerJob } from '@vue/reactivity'
2+
import { invokeArrayFns } from '@vue/shared'
3+
import { getCurrentInstance, setCurrentInstance } from './component'
4+
import { queueJob, queuePostRenderEffect } from './scheduler'
5+
import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
6+
import { invokeDirectiveHook } from './directives'
7+
8+
export function renderEffect(cb: () => void) {
9+
const instance = getCurrentInstance()
10+
11+
let effect: ReactiveEffect
12+
13+
const job: SchedulerJob = () => {
14+
if (!(effect.flags & EffectFlags.ACTIVE) || !effect.dirty) {
15+
return
16+
}
17+
18+
if (instance?.isMounted && !instance.isUpdating) {
19+
instance.isUpdating = true
20+
21+
const { bu, u, dirs } = instance
22+
// beforeUpdate hook
23+
if (bu) {
24+
invokeArrayFns(bu)
25+
}
26+
if (dirs) {
27+
invokeDirectiveHook(instance, 'beforeUpdate')
28+
}
29+
30+
effect.run()
31+
32+
queuePostRenderEffect(() => {
33+
instance.isUpdating = false
34+
if (dirs) {
35+
invokeDirectiveHook(instance, 'updated')
36+
}
37+
// updated hook
38+
if (u) {
39+
queuePostRenderEffect(u)
40+
}
41+
})
42+
} else {
43+
effect.run()
44+
}
45+
}
46+
47+
effect = new ReactiveEffect(() => {
48+
const reset = instance && setCurrentInstance(instance)
49+
callWithAsyncErrorHandling(cb, instance, VaporErrorCodes.RENDER_FUNCTION)
50+
reset?.()
51+
})
52+
53+
effect.scheduler = () => {
54+
if (instance) job.id = instance.uid
55+
queueJob(job)
56+
}
57+
58+
effect.run()
59+
}

packages/runtime-vapor/src/renderWatch.ts

Lines changed: 0 additions & 116 deletions
This file was deleted.

packages/runtime-vapor/src/scheduler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ let postFlushIndex = 0
3232
const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise<any>
3333
let currentFlushPromise: Promise<void> | null = null
3434

35-
function queueJob(job: SchedulerJob) {
35+
export function queueJob(job: SchedulerJob) {
3636
if (!(job.flags! & SchedulerJobFlags.QUEUED)) {
3737
if (job.id == null) {
3838
queue.push(job)

0 commit comments

Comments
 (0)