Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1e598cd

Browse files
committedApr 6, 2025·
refactor(runtime-vapor): rewrite renderEffect as a class
1 parent 1b0d881 commit 1e598cd

File tree

3 files changed

+92
-55
lines changed

3 files changed

+92
-55
lines changed
 

Diff for: ‎packages/reactivity/__tests__/effect.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -624,15 +624,15 @@ describe('reactivity/effect', () => {
624624
const runner = effect(() => {})
625625
const otherRunner = effect(runner)
626626
expect(runner).not.toBe(otherRunner)
627-
expect(runner.effect.fn).toBe(otherRunner.effect.fn)
627+
expect(runner.effect.callback).toBe(otherRunner.effect.callback)
628628
})
629629

630630
it('should wrap if the passed function is a fake effect', () => {
631631
const fakeRunner = () => {}
632632
fakeRunner.effect = {}
633633
const runner = effect(fakeRunner)
634634
expect(fakeRunner).not.toBe(runner)
635-
expect(runner.effect.fn).toBe(fakeRunner)
635+
expect(runner.effect.callback).toBe(fakeRunner)
636636
})
637637

638638
it('should not run multiple times for a single mutation', () => {

Diff for: ‎packages/reactivity/src/effect.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,13 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
7373
onTrack?: (event: DebuggerEvent) => void
7474
onTrigger?: (event: DebuggerEvent) => void
7575

76-
constructor(public fn: () => T) {
76+
// @ts-expect-error
77+
callback(): T {}
78+
79+
constructor(fn?: () => T) {
80+
if (fn !== undefined) {
81+
this.callback = fn
82+
}
7783
if (activeEffectScope && activeEffectScope.active) {
7884
link(this, activeEffectScope)
7985
}
@@ -120,15 +126,15 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
120126

121127
if (!this.active) {
122128
// stopped during cleanup
123-
return this.fn()
129+
return this.callback()
124130
}
125131
cleanupEffect(this)
126132
const prevSub = activeSub
127133
setActiveSub(this)
128134
startTracking(this)
129135

130136
try {
131-
return this.fn()
137+
return this.callback()
132138
} finally {
133139
if (__DEV__ && activeSub !== this) {
134140
warn(
@@ -191,7 +197,7 @@ export function effect<T = any>(
191197
options?: ReactiveEffectOptions,
192198
): ReactiveEffectRunner<T> {
193199
if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect) {
194-
fn = (fn as ReactiveEffectRunner).effect.fn
200+
fn = (fn as ReactiveEffectRunner).effect.callback
195201
}
196202

197203
const e = new ReactiveEffect(fn)

Diff for: ‎packages/runtime-vapor/src/renderEffect.ts

+80-49
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { ReactiveEffect, getCurrentScope } from '@vue/reactivity'
1+
import {
2+
type EffectScope,
3+
ReactiveEffect,
4+
getCurrentScope,
5+
} from '@vue/reactivity'
26
import {
37
type SchedulerJob,
48
currentInstance,
@@ -11,60 +15,87 @@ import {
1115
import { type VaporComponentInstance, isVaporComponent } from './component'
1216
import { invokeArrayFns } from '@vue/shared'
1317

14-
export function renderEffect(fn: () => void, noLifecycle = false): void {
15-
const instance = currentInstance as VaporComponentInstance | null
16-
const scope = getCurrentScope()
17-
if (__DEV__ && !__TEST__ && !scope && !isVaporComponent(instance)) {
18-
warn('renderEffect called without active EffectScope or Vapor instance.')
19-
}
18+
class RenderEffect extends ReactiveEffect {
19+
i: VaporComponentInstance | null
20+
scope: EffectScope | undefined
21+
baseJob: SchedulerJob
22+
postJob: SchedulerJob
23+
24+
constructor(
25+
private renderFn: () => void,
26+
private noLifecycle = false,
27+
) {
28+
super()
29+
const instance = currentInstance as VaporComponentInstance | null
30+
const scope = getCurrentScope()
31+
if (__DEV__ && !__TEST__ && !scope && !isVaporComponent(instance)) {
32+
warn('renderEffect called without active EffectScope or Vapor instance.')
33+
}
34+
35+
this.baseJob = () => {
36+
if (this.dirty) {
37+
this.run()
38+
}
39+
}
40+
this.postJob = () => {
41+
instance!.isUpdating = false
42+
instance!.u && invokeArrayFns(instance!.u)
43+
}
2044

21-
// renderEffect is always called after user has registered all hooks
22-
const hasUpdateHooks = instance && (instance.bu || instance.u)
23-
const renderEffectFn = noLifecycle
24-
? fn
25-
: () => {
26-
if (__DEV__ && instance) {
27-
startMeasure(instance, `renderEffect`)
28-
}
29-
const prev = currentInstance
30-
simpleSetCurrentInstance(instance)
31-
if (scope) scope.on()
32-
if (hasUpdateHooks && instance.isMounted && !instance.isUpdating) {
33-
instance.isUpdating = true
34-
instance.bu && invokeArrayFns(instance.bu)
35-
fn()
36-
queuePostFlushCb(() => {
37-
instance.isUpdating = false
38-
instance.u && invokeArrayFns(instance.u)
39-
})
40-
} else {
41-
fn()
42-
}
43-
if (scope) scope.off()
44-
simpleSetCurrentInstance(prev, instance)
45-
if (__DEV__ && instance) {
46-
startMeasure(instance, `renderEffect`)
47-
}
45+
if (instance) {
46+
if (__DEV__) {
47+
this.onTrack = instance.rtc
48+
? e => invokeArrayFns(instance.rtc!, e)
49+
: void 0
50+
this.onTrigger = instance.rtg
51+
? e => invokeArrayFns(instance.rtg!, e)
52+
: void 0
4853
}
54+
this.baseJob.i = instance
55+
this.baseJob.id = instance.uid
56+
}
57+
58+
this.i = instance
59+
this.scope = scope
4960

50-
const effect = new ReactiveEffect(renderEffectFn)
51-
const job: SchedulerJob = () => effect.dirty && effect.run()
61+
// TODO recurse handling
62+
}
5263

53-
if (instance) {
54-
if (__DEV__) {
55-
effect.onTrack = instance.rtc
56-
? e => invokeArrayFns(instance.rtc!, e)
57-
: void 0
58-
effect.onTrigger = instance.rtg
59-
? e => invokeArrayFns(instance.rtg!, e)
60-
: void 0
64+
callback() {
65+
if (this.noLifecycle) {
66+
this.renderFn()
67+
return
68+
}
69+
const instance = this.i!
70+
const scope = this.scope
71+
// renderEffect is always called after user has registered all hooks
72+
const hasUpdateHooks = instance && (instance.bu || instance.u)
73+
if (__DEV__ && instance) {
74+
startMeasure(instance, `renderEffect`)
75+
}
76+
const prev = currentInstance
77+
simpleSetCurrentInstance(instance)
78+
if (scope) scope.on()
79+
if (hasUpdateHooks && instance.isMounted && !instance.isUpdating) {
80+
instance.isUpdating = true
81+
instance.bu && invokeArrayFns(instance.bu)
82+
this.renderFn()
83+
queuePostFlushCb(this.postJob)
84+
} else {
85+
this.renderFn()
86+
}
87+
if (scope) scope.off()
88+
simpleSetCurrentInstance(prev, instance)
89+
if (__DEV__ && instance) {
90+
startMeasure(instance, `renderEffect`)
6191
}
62-
job.i = instance
63-
job.id = instance.uid
6492
}
6593

66-
effect.scheduler = () => queueJob(job)
67-
effect.run()
94+
scheduler() {
95+
queueJob(this.baseJob)
96+
}
97+
}
6898

69-
// TODO recurse handling
99+
export function renderEffect(fn: () => void, noLifecycle = false): void {
100+
new RenderEffect(fn, noLifecycle).run()
70101
}

0 commit comments

Comments
 (0)
Please sign in to comment.