Skip to content

Commit 14e65c2

Browse files
committed
refactor: make effect scope enable/disable logic symmetric
1 parent 481df0e commit 14e65c2

File tree

12 files changed

+72
-86
lines changed

12 files changed

+72
-86
lines changed

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

+2-15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
onScopeDispose,
1010
reactive,
1111
ref,
12+
setCurrentScope,
1213
} from '../src'
1314

1415
describe('reactivity/effect/scope', () => {
@@ -289,21 +290,7 @@ describe('reactivity/effect/scope', () => {
289290

290291
parentScope.run(() => {
291292
const childScope = effectScope(true)
292-
childScope.on()
293-
childScope.off()
294-
expect(getCurrentScope()).toBe(parentScope)
295-
})
296-
})
297-
298-
it('calling on() and off() multiple times inside an active scope should not break currentScope', () => {
299-
const parentScope = effectScope()
300-
parentScope.run(() => {
301-
const childScope = effectScope(true)
302-
childScope.on()
303-
childScope.on()
304-
childScope.off()
305-
childScope.off()
306-
childScope.off()
293+
setCurrentScope(setCurrentScope(childScope))
307294
expect(getCurrentScope()).toBe(parentScope)
308295
})
309296
})

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

+8-27
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ export class EffectScope implements Subscriber, Dependency {
2020
subs: Link | undefined = undefined
2121
subsTail: Link | undefined = undefined
2222

23-
/**
24-
* @internal track `on` calls, allow `on` call multiple times
25-
*/
26-
private _on = 0
2723
/**
2824
* @internal
2925
*/
@@ -87,29 +83,6 @@ export class EffectScope implements Subscriber, Dependency {
8783
}
8884
}
8985

90-
prevScope: EffectScope | undefined
91-
/**
92-
* This should only be called on non-detached scopes
93-
* @internal
94-
*/
95-
on(): void {
96-
if (++this._on === 1) {
97-
this.prevScope = activeEffectScope
98-
activeEffectScope = this
99-
}
100-
}
101-
102-
/**
103-
* This should only be called on non-detached scopes
104-
* @internal
105-
*/
106-
off(): void {
107-
if (this._on > 0 && --this._on === 0) {
108-
activeEffectScope = this.prevScope
109-
this.prevScope = undefined
110-
}
111-
}
112-
11386
stop(): void {
11487
if (this.active) {
11588
this.flags |= EffectFlags.STOP
@@ -150,6 +123,14 @@ export function getCurrentScope(): EffectScope | undefined {
150123
return activeEffectScope
151124
}
152125

126+
export function setCurrentScope(
127+
scope: EffectScope | undefined,
128+
): EffectScope | undefined {
129+
const prevScope = activeEffectScope
130+
activeEffectScope = scope
131+
return prevScope
132+
}
133+
153134
/**
154135
* Registers a dispose callback on the current active effect scope. The
155136
* callback will be invoked when the associated effect scope is stopped.

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

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ export {
7676
effectScope,
7777
EffectScope,
7878
getCurrentScope,
79+
/**
80+
* @internal
81+
*/
82+
setCurrentScope,
7983
onScopeDispose,
8084
} from './effectScope'
8185
export { reactiveReadArray, shallowReadArray } from './arrayInstrumentations'

Diff for: packages/runtime-core/src/apiSetupHelpers.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
createSetupContext,
1515
getCurrentGenericInstance,
1616
setCurrentInstance,
17-
unsetCurrentInstance,
17+
simpleSetCurrentInstance,
1818
} from './component'
1919
import type { EmitFn, EmitsOptions, ObjectEmitsOptions } from './componentEmits'
2020
import type {
@@ -31,7 +31,7 @@ import type {
3131
} from './componentProps'
3232
import { warn } from './warning'
3333
import type { SlotsType, StrictUnwrapSlotsType } from './componentSlots'
34-
import type { Ref } from '@vue/reactivity'
34+
import { type Ref, setCurrentScope } from '@vue/reactivity'
3535

3636
// dev only
3737
const warnRuntimeUsage = (method: string) =>
@@ -511,7 +511,8 @@ export function withAsyncContext(getAwaitable: () => any): [any, () => void] {
511511
)
512512
}
513513
let awaitable = getAwaitable()
514-
unsetCurrentInstance()
514+
setCurrentScope(undefined)
515+
simpleSetCurrentInstance(null)
515516
if (isPromise(awaitable)) {
516517
awaitable = awaitable.catch(e => {
517518
setCurrentInstance(ctx)

Diff for: packages/runtime-core/src/component.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
pauseTracking,
99
proxyRefs,
1010
resetTracking,
11+
setCurrentScope,
1112
shallowReadonly,
1213
track,
1314
} from '@vue/reactivity'
@@ -97,7 +98,7 @@ import type { RendererElement } from './renderer'
9798
import {
9899
setCurrentInstance,
99100
setInSSRSetupState,
100-
unsetCurrentInstance,
101+
simpleSetCurrentInstance,
101102
} from './componentCurrentInstance'
102103

103104
export * from './componentCurrentInstance'
@@ -911,6 +912,10 @@ function setupStatefulComponent(
911912
}
912913

913914
if (isAsyncSetup) {
915+
const unsetCurrentInstance = (): void => {
916+
setCurrentScope(undefined)
917+
simpleSetCurrentInstance(null)
918+
}
914919
setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
915920
if (isSSR) {
916921
// return the promise so server-renderer can wait on it

Diff for: packages/runtime-core/src/componentCurrentInstance.ts

+11-29
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
GenericComponentInstance,
55
} from './component'
66
import { currentRenderingInstance } from './componentRenderContext'
7+
import { setCurrentScope } from '@vue/reactivity'
78

89
/**
910
* @internal
@@ -25,7 +26,10 @@ export let isInSSRComponentSetup = false
2526

2627
export let setInSSRSetupState: (state: boolean) => void
2728

28-
let internalSetCurrentInstance: (
29+
/**
30+
* @internal
31+
*/
32+
export let simpleSetCurrentInstance: (
2933
instance: GenericComponentInstance | null,
3034
) => void
3135

@@ -53,7 +57,7 @@ if (__SSR__) {
5357
else setters[0](v)
5458
}
5559
}
56-
internalSetCurrentInstance = registerGlobalSetter(
60+
simpleSetCurrentInstance = registerGlobalSetter(
5761
`__VUE_INSTANCE_SETTERS__`,
5862
v => (currentInstance = v),
5963
)
@@ -66,7 +70,7 @@ if (__SSR__) {
6670
v => (isInSSRComponentSetup = v),
6771
)
6872
} else {
69-
internalSetCurrentInstance = i => {
73+
simpleSetCurrentInstance = i => {
7074
currentInstance = i
7175
}
7276
setInSSRSetupState = v => {
@@ -76,32 +80,10 @@ if (__SSR__) {
7680

7781
export const setCurrentInstance = (instance: GenericComponentInstance) => {
7882
const prev = currentInstance
79-
internalSetCurrentInstance(instance)
80-
instance.scope.on()
83+
simpleSetCurrentInstance(instance)
84+
const prevScope = setCurrentScope(instance.scope)
8185
return (): void => {
82-
instance.scope.off()
83-
internalSetCurrentInstance(prev)
84-
}
85-
}
86-
87-
export const unsetCurrentInstance = (): void => {
88-
currentInstance && currentInstance.scope.off()
89-
internalSetCurrentInstance(null)
90-
}
91-
92-
/**
93-
* Exposed for vapor only. Vapor never runs during SSR so we don't want to pay
94-
* for the extra overhead
95-
* @internal
96-
*/
97-
export const simpleSetCurrentInstance = (
98-
i: GenericComponentInstance | null,
99-
unset?: GenericComponentInstance | null,
100-
): void => {
101-
currentInstance = i
102-
if (unset) {
103-
unset.scope.off()
104-
} else if (i) {
105-
i.scope.on()
86+
setCurrentScope(prevScope)
87+
simpleSetCurrentInstance(prev)
10688
}
10789
}

Diff for: packages/runtime-core/src/renderer.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import {
5959
ReactiveEffect,
6060
pauseTracking,
6161
resetTracking,
62+
setCurrentScope,
6263
} from '@vue/reactivity'
6364
import { updateProps } from './componentProps'
6465
import { updateSlots } from './componentSlots'
@@ -1594,9 +1595,9 @@ function baseCreateRenderer(
15941595
}
15951596

15961597
// create reactive effect for rendering
1597-
instance.scope.on()
1598+
const prevScope = setCurrentScope(instance.scope)
15981599
const effect = (instance.effect = new ReactiveEffect(componentUpdateFn))
1599-
instance.scope.off()
1600+
setCurrentScope(prevScope)
16001601

16011602
const update = (instance.update = effect.run.bind(effect))
16021603
const job: SchedulerJob = (instance.job = effect.scheduler.bind(effect))

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

+3
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ import {
1717
ref,
1818
simpleSetCurrentInstance,
1919
} from '@vue/runtime-dom'
20+
import { setCurrentScope } from '@vue/reactivity'
2021

2122
let removeComponentInstance = NOOP
2223
beforeEach(() => {
2324
const instance = new VaporComponentInstance({}, {}, null)
2425
const prev = currentInstance
2526
simpleSetCurrentInstance(instance)
27+
const prevScope = setCurrentScope(instance.scope)
2628
removeComponentInstance = () => {
29+
setCurrentScope(prevScope)
2730
simpleSetCurrentInstance(prev)
2831
}
2932
})

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
pauseTracking,
3434
proxyRefs,
3535
resetTracking,
36+
setCurrentScope,
3637
unref,
3738
} from '@vue/reactivity'
3839
import { EMPTY_OBJ, invokeArrayFns, isFunction, isString } from '@vue/shared'
@@ -193,6 +194,7 @@ export function createComponent(
193194

194195
const prev = currentInstance
195196
simpleSetCurrentInstance(instance)
197+
const prevScope = setCurrentScope(instance.scope)
196198
pauseTracking()
197199

198200
if (__DEV__) {
@@ -260,7 +262,8 @@ export function createComponent(
260262
}
261263

262264
resetTracking()
263-
simpleSetCurrentInstance(prev, instance)
265+
setCurrentScope(prevScope)
266+
simpleSetCurrentInstance(prev)
264267

265268
if (__DEV__) {
266269
popWarningContext()

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from '@vue/runtime-dom'
2424
import { normalizeEmitsOptions } from './componentEmits'
2525
import { renderEffect } from './renderEffect'
26+
import { setCurrentScope } from '@vue/reactivity'
2627

2728
export type RawProps = Record<string, () => unknown> & {
2829
// generated by compiler for :[key]="x" or v-bind="x"
@@ -259,8 +260,10 @@ function resolveDefault(
259260
) {
260261
const prev = currentInstance
261262
simpleSetCurrentInstance(instance)
263+
const prevScope = setCurrentScope(instance.scope)
262264
const res = factory.call(null, instance.props)
263-
simpleSetCurrentInstance(prev, instance)
265+
setCurrentScope(prevScope)
266+
simpleSetCurrentInstance(prev)
264267
return res
265268
}
266269

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
mountComponent,
1414
unmountComponent,
1515
} from './component'
16+
import { setCurrentScope } from '@vue/reactivity'
1617

1718
export function hmrRerender(instance: VaporComponentInstance): void {
1819
const normalized = normalizeBlock(instance.block)
@@ -21,10 +22,12 @@ export function hmrRerender(instance: VaporComponentInstance): void {
2122
remove(instance.block, parent)
2223
const prev = currentInstance
2324
simpleSetCurrentInstance(instance)
25+
const prevScope = setCurrentScope(instance.scope)
2426
pushWarningContext(instance)
2527
devRender(instance)
2628
popWarningContext()
27-
simpleSetCurrentInstance(prev, instance)
29+
setCurrentScope(prevScope)
30+
simpleSetCurrentInstance(prev)
2831
insert(instance.block, parent, anchor)
2932
}
3033

@@ -38,12 +41,19 @@ export function hmrReload(
3841
unmountComponent(instance, parent)
3942
const prev = currentInstance
4043
simpleSetCurrentInstance(instance.parent)
44+
let prevScope: any
45+
if (instance.parent !== null) {
46+
prevScope = setCurrentScope(instance.parent.scope)
47+
}
4148
const newInstance = createComponent(
4249
newComp,
4350
instance.rawProps,
4451
instance.rawSlots,
4552
instance.isSingleRoot,
4653
)
47-
simpleSetCurrentInstance(prev, instance.parent)
54+
if (instance.parent !== null) {
55+
setCurrentScope(prevScope)
56+
}
57+
simpleSetCurrentInstance(prev)
4858
mountComponent(newInstance, parent, anchor)
4959
}

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

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { EffectFlags, type EffectScope, ReactiveEffect } from '@vue/reactivity'
1+
import {
2+
EffectFlags,
3+
type EffectScope,
4+
ReactiveEffect,
5+
setCurrentScope,
6+
} from '@vue/reactivity'
27
import {
38
type SchedulerJob,
49
currentInstance,
@@ -49,7 +54,7 @@ class RenderEffect extends ReactiveEffect {
4954
}
5055

5156
callback(): void {
52-
const instance = this.i!
57+
const instance = this.i
5358
const scope = this.subs ? (this.subs.sub as EffectScope) : undefined
5459
// renderEffect is always called after user has registered all hooks
5560
const hasUpdateHooks = instance && (instance.bu || instance.u)
@@ -58,7 +63,8 @@ class RenderEffect extends ReactiveEffect {
5863
}
5964
const prev = currentInstance
6065
simpleSetCurrentInstance(instance)
61-
if (scope) scope.on()
66+
let prevScope: any
67+
if (scope) prevScope = setCurrentScope(scope)
6268
if (hasUpdateHooks && instance.isMounted && !instance.isUpdating) {
6369
instance.isUpdating = true
6470
instance.bu && invokeArrayFns(instance.bu)
@@ -67,8 +73,8 @@ class RenderEffect extends ReactiveEffect {
6773
} else {
6874
this.render()
6975
}
70-
if (scope) scope.off()
71-
simpleSetCurrentInstance(prev, instance)
76+
if (scope) setCurrentScope(prevScope)
77+
simpleSetCurrentInstance(prev)
7278
if (__DEV__ && instance) {
7379
startMeasure(instance, `renderEffect`)
7480
}

0 commit comments

Comments
 (0)