Skip to content

Commit 9e61c83

Browse files
committed
refactor(reactivity, runtime-core): improve performance-critical code
1 parent 9ab8e4c commit 9e61c83

40 files changed

+1407
-1385
lines changed

packages/reactivity/__tests__/computed.spec.ts

+3-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
} from '../src'
2828
import type { ComputedRef, ComputedRefImpl } from '../src/computed'
2929
import { pauseTracking, resetTracking } from '../src/effect'
30-
import { SubscriberFlags } from '../src/system'
30+
import { ReactiveFlags } from '../src/system'
3131

3232
describe('reactivity/computed', () => {
3333
it('should return updated value', () => {
@@ -467,12 +467,8 @@ describe('reactivity/computed', () => {
467467
const c2 = computed(() => c1.value) as unknown as ComputedRefImpl
468468

469469
c2.value
470-
expect(
471-
c1.flags & (SubscriberFlags.Dirty | SubscriberFlags.PendingComputed),
472-
).toBe(0)
473-
expect(
474-
c2.flags & (SubscriberFlags.Dirty | SubscriberFlags.PendingComputed),
475-
).toBe(0)
470+
expect(c1.flags & (ReactiveFlags.Dirty | ReactiveFlags.Pending)).toBe(0)
471+
expect(c2.flags & (ReactiveFlags.Dirty | ReactiveFlags.Pending)).toBe(0)
476472
})
477473

478474
it('should chained computeds dirtyLevel update with first computed effect', () => {

packages/reactivity/__tests__/effect.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
stop,
2323
toRaw,
2424
} from '../src/index'
25-
import { type Dependency, endBatch, startBatch } from '../src/system'
25+
import { type ReactiveNode, endBatch, startBatch } from '../src/system'
2626

2727
describe('reactivity/effect', () => {
2828
it('should run the passed function once (wrapped by a effect)', () => {
@@ -1178,7 +1178,7 @@ describe('reactivity/effect', () => {
11781178
})
11791179

11801180
describe('dep unsubscribe', () => {
1181-
function getSubCount(dep: Dependency | undefined) {
1181+
function getSubCount(dep: ReactiveNode | undefined) {
11821182
let count = 0
11831183
let sub = dep!.subs
11841184
while (sub) {

packages/reactivity/__tests__/effectScope.spec.ts

+32-34
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { nextTick, watch, watchEffect } from '@vue/runtime-core'
22
import {
33
type ComputedRef,
44
EffectScope,
5+
ReactiveEffect,
56
computed,
67
effect,
78
effectScope,
89
getCurrentScope,
910
onScopeDispose,
1011
reactive,
1112
ref,
13+
setCurrentScope,
1214
} from '../src'
1315

1416
describe('reactivity/effect/scope', () => {
@@ -20,7 +22,7 @@ describe('reactivity/effect/scope', () => {
2022

2123
it('should accept zero argument', () => {
2224
const scope = effectScope()
23-
expect(scope.effects.length).toBe(0)
25+
expect(getEffectsCount(scope)).toBe(0)
2426
})
2527

2628
it('should return run value', () => {
@@ -29,7 +31,8 @@ describe('reactivity/effect/scope', () => {
2931

3032
it('should work w/ active property', () => {
3133
const scope = effectScope()
32-
scope.run(() => 1)
34+
const src = computed(() => 1)
35+
scope.run(() => src.value)
3336
expect(scope.active).toBe(true)
3437
scope.stop()
3538
expect(scope.active).toBe(false)
@@ -47,7 +50,7 @@ describe('reactivity/effect/scope', () => {
4750
expect(dummy).toBe(7)
4851
})
4952

50-
expect(scope.effects.length).toBe(1)
53+
expect(getEffectsCount(scope)).toBe(1)
5154
})
5255

5356
it('stop', () => {
@@ -60,7 +63,7 @@ describe('reactivity/effect/scope', () => {
6063
effect(() => (doubled = counter.num * 2))
6164
})
6265

63-
expect(scope.effects.length).toBe(2)
66+
expect(getEffectsCount(scope)).toBe(2)
6467

6568
expect(dummy).toBe(0)
6669
counter.num = 7
@@ -87,9 +90,8 @@ describe('reactivity/effect/scope', () => {
8790
})
8891
})
8992

90-
expect(scope.effects.length).toBe(1)
91-
expect(scope.scopes!.length).toBe(1)
92-
expect(scope.scopes![0]).toBeInstanceOf(EffectScope)
93+
expect(getEffectsCount(scope)).toBe(1)
94+
expect(scope.deps?.nextDep?.dep).toBeInstanceOf(EffectScope)
9395

9496
expect(dummy).toBe(0)
9597
counter.num = 7
@@ -117,7 +119,7 @@ describe('reactivity/effect/scope', () => {
117119
})
118120
})
119121

120-
expect(scope.effects.length).toBe(1)
122+
expect(getEffectsCount(scope)).toBe(1)
121123

122124
expect(dummy).toBe(0)
123125
counter.num = 7
@@ -142,13 +144,13 @@ describe('reactivity/effect/scope', () => {
142144
effect(() => (dummy = counter.num))
143145
})
144146

145-
expect(scope.effects.length).toBe(1)
147+
expect(getEffectsCount(scope)).toBe(1)
146148

147149
scope.run(() => {
148150
effect(() => (doubled = counter.num * 2))
149151
})
150152

151-
expect(scope.effects.length).toBe(2)
153+
expect(getEffectsCount(scope)).toBe(2)
152154

153155
counter.num = 7
154156
expect(dummy).toBe(7)
@@ -166,21 +168,21 @@ describe('reactivity/effect/scope', () => {
166168
effect(() => (dummy = counter.num))
167169
})
168170

169-
expect(scope.effects.length).toBe(1)
171+
expect(getEffectsCount(scope)).toBe(1)
170172

171173
scope.stop()
172174

175+
expect(getEffectsCount(scope)).toBe(0)
176+
173177
scope.run(() => {
174178
effect(() => (doubled = counter.num * 2))
175179
})
176180

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

181183
counter.num = 7
182184
expect(dummy).toBe(0)
183-
expect(doubled).toBe(undefined)
185+
expect(doubled).toBe(14)
184186
})
185187

186188
it('should fire onScopeDispose hook', () => {
@@ -224,9 +226,9 @@ describe('reactivity/effect/scope', () => {
224226
it('should dereference child scope from parent scope after stopping child scope (no memleaks)', () => {
225227
const parent = effectScope()
226228
const child = parent.run(() => effectScope())!
227-
expect(parent.scopes!.includes(child)).toBe(true)
229+
expect(parent.deps?.dep).toBe(child)
228230
child.stop()
229-
expect(parent.scopes!.includes(child)).toBe(false)
231+
expect(parent.deps).toBeUndefined()
230232
})
231233

232234
it('test with higher level APIs', async () => {
@@ -290,21 +292,7 @@ describe('reactivity/effect/scope', () => {
290292

291293
parentScope.run(() => {
292294
const childScope = effectScope(true)
293-
childScope.on()
294-
childScope.off()
295-
expect(getCurrentScope()).toBe(parentScope)
296-
})
297-
})
298-
299-
it('calling on() and off() multiple times inside an active scope should not break currentScope', () => {
300-
const parentScope = effectScope()
301-
parentScope.run(() => {
302-
const childScope = effectScope(true)
303-
childScope.on()
304-
childScope.on()
305-
childScope.off()
306-
childScope.off()
307-
childScope.off()
295+
setCurrentScope(setCurrentScope(childScope))
308296
expect(getCurrentScope()).toBe(parentScope)
309297
})
310298
})
@@ -372,7 +360,17 @@ describe('reactivity/effect/scope', () => {
372360
expect(watcherCalls).toBe(3)
373361
expect(cleanupCalls).toBe(1)
374362

375-
expect(scope.effects.length).toBe(0)
376-
expect(scope.cleanups.length).toBe(0)
363+
expect(getEffectsCount(scope)).toBe(0)
364+
expect(scope.cleanupsLength).toBe(0)
377365
})
378366
})
367+
368+
function getEffectsCount(scope: EffectScope): number {
369+
let n = 0
370+
for (let dep = scope.deps; dep !== undefined; dep = dep.nextDep) {
371+
if (dep.dep instanceof ReactiveEffect) {
372+
n++
373+
}
374+
}
375+
return n
376+
}

packages/reactivity/__tests__/watch.spec.ts

-76
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,12 @@ import {
33
type Ref,
44
WatchErrorCodes,
55
type WatchOptions,
6-
type WatchScheduler,
76
computed,
87
onWatcherCleanup,
98
ref,
109
watch,
1110
} from '../src'
1211

13-
const queue: (() => void)[] = []
14-
15-
// a simple scheduler for testing purposes
16-
let isFlushPending = false
17-
const resolvedPromise = /*@__PURE__*/ Promise.resolve() as Promise<any>
18-
const nextTick = (fn?: () => any) =>
19-
fn ? resolvedPromise.then(fn) : resolvedPromise
20-
21-
const scheduler: WatchScheduler = (job, isFirstRun) => {
22-
if (isFirstRun) {
23-
job()
24-
} else {
25-
queue.push(job)
26-
flushJobs()
27-
}
28-
}
29-
30-
const flushJobs = () => {
31-
if (isFlushPending) return
32-
isFlushPending = true
33-
resolvedPromise.then(() => {
34-
queue.forEach(job => job())
35-
queue.length = 0
36-
isFlushPending = false
37-
})
38-
}
39-
4012
describe('watch', () => {
4113
test('effect', () => {
4214
let dummy: any
@@ -147,54 +119,6 @@ describe('watch', () => {
147119
expect(dummy).toBe(30)
148120
})
149121

150-
test('nested calls to baseWatch and onWatcherCleanup', async () => {
151-
let calls: string[] = []
152-
let source: Ref<number>
153-
let copyist: Ref<number>
154-
const scope = new EffectScope()
155-
156-
scope.run(() => {
157-
source = ref(0)
158-
copyist = ref(0)
159-
// sync by default
160-
watch(
161-
() => {
162-
const current = (copyist.value = source.value)
163-
onWatcherCleanup(() => calls.push(`sync ${current}`))
164-
},
165-
null,
166-
{},
167-
)
168-
// with scheduler
169-
watch(
170-
() => {
171-
const current = copyist.value
172-
onWatcherCleanup(() => calls.push(`post ${current}`))
173-
},
174-
null,
175-
{ scheduler },
176-
)
177-
})
178-
179-
await nextTick()
180-
expect(calls).toEqual([])
181-
182-
scope.run(() => source.value++)
183-
expect(calls).toEqual(['sync 0'])
184-
await nextTick()
185-
expect(calls).toEqual(['sync 0', 'post 0'])
186-
calls.length = 0
187-
188-
scope.run(() => source.value++)
189-
expect(calls).toEqual(['sync 1'])
190-
await nextTick()
191-
expect(calls).toEqual(['sync 1', 'post 1'])
192-
calls.length = 0
193-
194-
scope.stop()
195-
expect(calls).toEqual(['sync 2', 'post 2'])
196-
})
197-
198122
test('once option should be ignored by simple watch', async () => {
199123
let dummy: any
200124
const source = ref(0)

0 commit comments

Comments
 (0)