Skip to content

Commit 9e65526

Browse files
committed
wip
1 parent 16e15c7 commit 9e65526

File tree

5 files changed

+114
-97
lines changed

5 files changed

+114
-97
lines changed

Diff for: packages/reactivity/__tests__/effectScope.spec.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ describe('reactivity/effect/scope', () => {
3030

3131
it('should work w/ active property', () => {
3232
const scope = effectScope()
33-
scope.run(() => 1)
33+
const src = computed(() => 1)
34+
scope.run(() => src.value)
3435
expect(scope.active).toBe(true)
3536
scope.stop()
3637
expect(scope.active).toBe(false)
@@ -170,13 +171,13 @@ describe('reactivity/effect/scope', () => {
170171

171172
scope.stop()
172173

174+
expect(getEffectsCount(scope)).toBe(0)
175+
173176
scope.run(() => {
174177
effect(() => (doubled = counter.num * 2))
175178
})
176179

177-
expect('[Vue warn] cannot run an inactive effect scope.').toHaveBeenWarned()
178-
179-
expect(getEffectsCount(scope)).toBe(0)
180+
expect(getEffectsCount(scope)).toBe(1)
180181

181182
counter.num = 7
182183
expect(dummy).toBe(0)
@@ -359,7 +360,7 @@ describe('reactivity/effect/scope', () => {
359360
expect(cleanupCalls).toBe(1)
360361

361362
expect(getEffectsCount(scope)).toBe(0)
362-
expect(scope.cleanups.length).toBe(0)
363+
expect(scope.cleanups).toBe(0)
363364
})
364365
})
365366

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

+47-41
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import type { TrackOpTypes, TriggerOpTypes } from './constants'
33
import { setupOnTrigger } from './debug'
44
import { activeEffectScope } from './effectScope'
55
import {
6+
type Dependency,
67
type Link,
78
type Subscriber,
89
SubscriberFlags,
910
checkDirty,
11+
cleanup,
1012
endTracking,
1113
link,
14+
onCleanup,
1215
startTracking,
1316
unlink,
1417
} from './system'
@@ -36,6 +39,9 @@ export interface DebuggerOptions {
3639

3740
export interface ReactiveEffectOptions extends DebuggerOptions {
3841
scheduler?: EffectScheduler
42+
/**
43+
* @deprecated
44+
*/
3945
onStop?: () => void
4046
}
4147

@@ -51,26 +57,24 @@ export enum EffectFlags {
5157
ALLOW_RECURSE = 1 << 7,
5258
PAUSED = 1 << 8,
5359
NOTIFIED = 1 << 9,
54-
STOP = 1 << 10,
5560
}
5661

57-
export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
62+
export class ReactiveEffect<T = any>
63+
implements ReactiveEffectOptions, Dependency, Subscriber
64+
{
5865
// Subscriber
5966
deps: Link | undefined = undefined
6067
depsTail: Link | undefined = undefined
6168
flags: number = 0
69+
cleanups: number = 0
6270

6371
// Dependency
6472
subs: Link | undefined = undefined
6573
subsTail: Link | undefined = undefined
6674

67-
/**
68-
* @internal
69-
*/
70-
cleanup?: () => void = undefined
71-
72-
onStop?: () => void
75+
// dev only
7376
onTrack?: (event: DebuggerEvent) => void
77+
// dev only
7478
onTrigger?: (event: DebuggerEvent) => void
7579

7680
// @ts-expect-error
@@ -80,13 +84,13 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
8084
if (fn !== undefined) {
8185
this.fn = fn
8286
}
83-
if (activeEffectScope && activeEffectScope.active) {
87+
if (activeEffectScope) {
8488
link(this, activeEffectScope)
8589
}
8690
}
8791

8892
get active(): boolean {
89-
return !(this.flags & EffectFlags.STOP)
93+
return this.deps !== undefined
9094
}
9195

9296
pause(): void {
@@ -122,12 +126,10 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
122126
}
123127

124128
run(): T {
125-
let flags = this.flags
126-
if (flags & EffectFlags.STOP) {
127-
// stopped during cleanup
128-
return this.fn()
129+
const cleanups = this.cleanups
130+
if (cleanups) {
131+
cleanup(this, cleanups)
129132
}
130-
cleanupEffect(this)
131133
const prevSub = activeSub
132134
setActiveSub(this)
133135
startTracking(this)
@@ -143,7 +145,7 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
143145
}
144146
setActiveSub(prevSub)
145147
endTracking(this)
146-
flags = this.flags
148+
const flags = this.flags
147149
if (
148150
(flags & (SubscriberFlags.Recursed | EffectFlags.ALLOW_RECURSE)) ===
149151
(SubscriberFlags.Recursed | EffectFlags.ALLOW_RECURSE)
@@ -155,17 +157,15 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
155157
}
156158

157159
stop(): void {
158-
const flags = this.flags
159-
if (!(flags & EffectFlags.STOP)) {
160-
this.flags = flags | EffectFlags.STOP
161-
startTracking(this)
162-
endTracking(this)
163-
cleanupEffect(this)
164-
this.onStop && this.onStop()
165-
166-
if (this.subs !== undefined) {
167-
unlink(this.subs)
168-
}
160+
const sub = this.subs
161+
const cleanups = this.cleanups
162+
if (sub !== undefined) {
163+
unlink(sub)
164+
}
165+
startTracking(this)
166+
endTracking(this)
167+
if (cleanups) {
168+
cleanup(this, cleanups)
169169
}
170170
}
171171

@@ -202,6 +202,15 @@ export function effect<T = any>(
202202

203203
const e = new ReactiveEffect(fn)
204204
if (options) {
205+
const onStop = options.onStop
206+
if (onStop) {
207+
delete options.onStop
208+
const stop = e.stop.bind(e)
209+
e.stop = () => {
210+
stop()
211+
onStop()
212+
}
213+
}
205214
extend(e, options)
206215
}
207216
try {
@@ -286,8 +295,9 @@ export function resetTracking(): void {
286295
* an active effect.
287296
*/
288297
export function onEffectCleanup(fn: () => void, failSilently = false): void {
289-
if (activeSub instanceof ReactiveEffect) {
290-
activeSub.cleanup = fn
298+
const e = activeSub
299+
if (e instanceof ReactiveEffect) {
300+
onCleanup(e, () => cleanupEffect(e, fn))
291301
} else if (__DEV__ && !failSilently) {
292302
warn(
293303
`onEffectCleanup() was called when there was no active effect` +
@@ -296,18 +306,14 @@ export function onEffectCleanup(fn: () => void, failSilently = false): void {
296306
}
297307
}
298308

299-
function cleanupEffect(e: ReactiveEffect) {
300-
const cleanup = e.cleanup
301-
if (cleanup !== undefined) {
302-
e.cleanup = undefined
303-
// run cleanup without active effect
304-
const prevSub = activeSub
305-
activeSub = undefined
306-
try {
307-
cleanup()
308-
} finally {
309-
activeSub = prevSub
310-
}
309+
function cleanupEffect(e: ReactiveEffect, fn: () => void) {
310+
// run cleanup without active effect
311+
const prevSub = activeSub
312+
activeSub = undefined
313+
try {
314+
fn()
315+
} finally {
316+
activeSub = prevSub
311317
}
312318
}
313319

Diff for: packages/reactivity/src/effectScope.ts

+22-36
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import {
33
type Dependency,
44
type Link,
55
type Subscriber,
6+
cleanup,
7+
endTracking,
68
link,
9+
onCleanup,
10+
startTracking,
711
unlink,
812
} from './system'
913
import { warn } from './warning'
@@ -15,28 +19,20 @@ export class EffectScope implements Subscriber, Dependency {
1519
deps: Link | undefined = undefined
1620
depsTail: Link | undefined = undefined
1721
flags: number = 0
22+
cleanups: number = 0
1823

1924
// Dependency
2025
subs: Link | undefined = undefined
2126
subsTail: Link | undefined = undefined
2227

23-
/**
24-
* @internal
25-
*/
26-
cleanups: (() => void)[] = []
27-
/**
28-
* @internal
29-
*/
30-
cleanupsLength = 0
31-
3228
constructor(detached = false) {
3329
if (!detached && activeEffectScope) {
3430
link(this, activeEffectScope)
3531
}
3632
}
3733

3834
get active(): boolean {
39-
return !(this.flags & EffectFlags.STOP)
35+
return this.deps !== undefined
4036
}
4137

4238
notify(): void {}
@@ -70,35 +66,25 @@ export class EffectScope implements Subscriber, Dependency {
7066
}
7167

7268
run<T>(fn: () => T): T | undefined {
73-
const flags = this.flags
74-
if (!(flags & EffectFlags.STOP)) {
75-
const prevEffectScope = activeEffectScope
76-
try {
77-
activeEffectScope = this
78-
return fn()
79-
} finally {
80-
activeEffectScope = prevEffectScope
81-
}
82-
} else if (__DEV__) {
83-
warn(`cannot run an inactive effect scope.`)
69+
const prevEffectScope = activeEffectScope
70+
try {
71+
activeEffectScope = this
72+
return fn()
73+
} finally {
74+
activeEffectScope = prevEffectScope
8475
}
8576
}
8677

8778
stop(): void {
88-
const flags = this.flags
89-
if (!(flags & EffectFlags.STOP)) {
90-
this.flags = flags | EffectFlags.STOP
91-
while (this.deps !== undefined) {
92-
unlink(this.deps)
93-
}
94-
const l = this.cleanupsLength
95-
for (let i = 0; i < l; i++) {
96-
this.cleanups[i]()
97-
}
98-
this.cleanupsLength = 0
99-
if (this.subs !== undefined) {
100-
unlink(this.subs)
101-
}
79+
const sub = this.subs
80+
const cleanups = this.cleanups
81+
if (sub !== undefined) {
82+
unlink(sub)
83+
}
84+
startTracking(this)
85+
endTracking(this)
86+
if (cleanups) {
87+
cleanup(this, cleanups)
10288
}
10389
}
10490
}
@@ -142,7 +128,7 @@ export function setCurrentScope(
142128
*/
143129
export function onScopeDispose(fn: () => void, failSilently = false): void {
144130
if (activeEffectScope) {
145-
activeEffectScope.cleanups[activeEffectScope.cleanupsLength++] = fn
131+
onCleanup(activeEffectScope, fn)
146132
} else if (__DEV__ && !failSilently) {
147133
warn(
148134
`onScopeDispose() is called when there is no active effect scope` +

0 commit comments

Comments
 (0)