Skip to content

Commit 1aa1059

Browse files
committed
wip
1 parent 59b1da8 commit 1aa1059

File tree

6 files changed

+85
-59
lines changed

6 files changed

+85
-59
lines changed

packages/reactivity/src/effectScope.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { EffectFlags, cleanup, onCleanup } from './effect'
1+
import { EffectFlags, ReactiveEffect, cleanup, onCleanup } from './effect'
22
import {
33
type Dependency,
44
type Link,
@@ -18,6 +18,8 @@ export class EffectScope implements Subscriber, Dependency {
1818
depsTail: Link | undefined = undefined
1919
flags: number = 0
2020
cleanups: number = 0
21+
jobs: Link | undefined = undefined
22+
jobsTail: Link | undefined = undefined
2123

2224
// Dependency
2325
subs: Link | undefined = undefined
@@ -33,7 +35,17 @@ export class EffectScope implements Subscriber, Dependency {
3335
return this.deps !== undefined
3436
}
3537

36-
notify(): void {}
38+
notify(): void {
39+
let link = this.jobs
40+
while (link !== undefined) {
41+
;(link.dep as ReactiveEffect | EffectScope).notify()
42+
const next = link.nextJob
43+
link.nextJob = undefined
44+
link = next
45+
}
46+
this.jobs = undefined
47+
this.jobsTail = undefined
48+
}
3749

3850
pause(): void {
3951
if (!(this.flags & EffectFlags.PAUSED)) {

packages/reactivity/src/system.ts

+54-12
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,33 @@ export interface Link {
2222
nextSub: Link | undefined
2323
prevDep: Link | undefined
2424
nextDep: Link | undefined
25+
nextJob: Link | undefined
2526
}
2627

2728
export const enum SubscriberFlags {
2829
Tracking = 1 << 2,
2930
Recursed = 1 << 4,
3031
Dirty = 1 << 5,
3132
Pending = 1 << 6,
33+
Job = 1 << 10,
3234
}
3335

3436
interface OneWayLink<T> {
3537
target: T
3638
linked: OneWayLink<T> | undefined
3739
}
3840

39-
const notifyBuffer: (Effect | EffectScope | undefined)[] = []
41+
const notifyBuffer: (Effect | undefined)[] = []
42+
const jobBuffer: (Effect | EffectScope | undefined)[] = []
4043

4144
let batchDepth = 0
4245
let notifyIndex = 0
46+
let flushIndex = 0
4347
let notifyBufferLength = 0
48+
let jobBufferLength = 0
49+
let currentFlushPromise: Promise<void> | undefined
50+
51+
const resolvedPromise = /*@__PURE__*/ Promise.resolve() as Promise<any>
4452

4553
export function startBatch(): void {
4654
++batchDepth
@@ -77,6 +85,7 @@ export function link(dep: Dependency, sub: Subscriber): void {
7785
nextDep,
7886
prevSub: undefined,
7987
nextSub: undefined,
88+
nextJob: undefined,
8089
}
8190
if (prevDep === undefined) {
8291
sub.deps = newLink
@@ -180,18 +189,33 @@ export function propagate(current: Link): void {
180189

181190
if (shouldNotify) {
182191
if (shouldNotify === 1 && 'notify' in sub) {
183-
notifyBuffer[notifyBufferLength++] = sub
184-
} else {
185-
const subSubs = (sub as Dependency).subs
186-
if (subSubs !== undefined) {
187-
current = subSubs
188-
if (subSubs.nextSub !== undefined) {
189-
branchs = { target: next, linked: branchs }
190-
++branchDepth
191-
next = current.nextSub
192+
if (subFlags & SubscriberFlags.Job) {
193+
let last = sub
194+
let parent = sub.subs
195+
let queued = false
196+
while (parent !== undefined) {
197+
last = parent.sub as EffectScope
198+
let next = last.subs
199+
while (last.deps === last.depsTail && next !== undefined) {
200+
last = next.sub as EffectScope
201+
next = last.subs
202+
}
203+
const jobsTail = last.jobsTail
204+
if (jobsTail === undefined) {
205+
last.jobsTail = last.jobs = parent
206+
parent = next
207+
} else {
208+
last.jobsTail = jobsTail.nextJob = parent
209+
queued = true
210+
break
211+
}
192212
}
193-
targetFlag = SubscriberFlags.Pending
194-
continue
213+
if (!queued) {
214+
jobBuffer[jobBufferLength++] = last
215+
queueFlush()
216+
}
217+
} else if (!('jobs' in sub)) {
218+
notifyBuffer[notifyBufferLength++] = sub
195219
}
196220
}
197221
} else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) {
@@ -230,6 +254,24 @@ export function propagate(current: Link): void {
230254
}
231255
}
232256

257+
async function queueFlush() {
258+
if (currentFlushPromise === undefined) {
259+
currentFlushPromise = resolvedPromise
260+
try {
261+
await currentFlushPromise
262+
while (flushIndex < jobBufferLength) {
263+
const effect = jobBuffer[flushIndex]!
264+
jobBuffer[flushIndex++] = undefined
265+
effect.notify()
266+
}
267+
flushIndex = 0
268+
jobBufferLength = 0
269+
} finally {
270+
currentFlushPromise = undefined
271+
}
272+
}
273+
}
274+
233275
export function startTracking(sub: Subscriber): void {
234276
sub.depsTail = undefined
235277
sub.flags =

packages/runtime-core/src/component.ts

-5
Original file line numberDiff line numberDiff line change
@@ -557,10 +557,6 @@ export interface ComponentInternalInstance extends GenericComponentInstance {
557557
* Force update render effect
558558
*/
559559
update: () => void
560-
/**
561-
* Render effect job to be passed to scheduler (checks if dirty)
562-
*/
563-
job: SchedulerJob
564560
/**
565561
* The render function that returns vdom tree.
566562
* @internal
@@ -718,7 +714,6 @@ export function createComponentInstance(
718714
subTree: null!, // will be set synchronously right after creation
719715
effect: null!,
720716
update: null!, // will be set synchronously right after creation
721-
job: null!,
722717
scope: new EffectScope(true /* detached */),
723718
render: null,
724719
proxy: null,

packages/runtime-core/src/components/BaseTransition.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { PatchFlags, ShapeFlags, isArray, isFunction } from '@vue/shared'
2020
import { onBeforeUnmount, onMounted } from '../apiLifecycle'
2121
import { isTeleport } from './Teleport'
2222
import type { RendererElement } from '../renderer'
23-
import { SchedulerJobFlags } from '../scheduler'
2423

2524
type Hook<T = () => void> = T | T[]
2625

@@ -223,7 +222,7 @@ const BaseTransitionImpl: ComponentOptions = {
223222
state.isLeaving = false
224223
// #6835
225224
// it also needs to be updated when active is undefined
226-
if (!(instance.job.flags! & SchedulerJobFlags.DISPOSED)) {
225+
if (instance.effect.active) {
227226
instance.update()
228227
}
229228
delete leavingHooks.afterLeave

packages/runtime-core/src/renderer.ts

+6-12
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,10 @@ import {
4646
isReservedProp,
4747
} from '@vue/shared'
4848
import {
49-
type SchedulerJob,
5049
SchedulerJobFlags,
5150
type SchedulerJobs,
5251
flushOnAppMount,
5352
flushPreFlushCbs,
54-
queueJob,
5553
queuePostFlushCb,
5654
} from './scheduler'
5755
import {
@@ -1597,13 +1595,11 @@ function baseCreateRenderer(
15971595
// create reactive effect for rendering
15981596
const prevScope = setCurrentScope(instance.scope)
15991597
const effect = (instance.effect = new ReactiveEffect(componentUpdateFn))
1598+
effect.flags |= 1 << 10 // SubscriberFlags.Job
1599+
16001600
setCurrentScope(prevScope)
16011601

16021602
const update = (instance.update = effect.run.bind(effect))
1603-
const job: SchedulerJob = (instance.job = effect.scheduler.bind(effect))
1604-
job.i = instance
1605-
job.id = instance.uid
1606-
effect.scheduler = () => queueJob(job)
16071603

16081604
// allowRecurse
16091605
// #1801, #2043 component render effects should allow recursive updates
@@ -2343,7 +2339,7 @@ function baseCreateRenderer(
23432339
unregisterHMR(instance)
23442340
}
23452341

2346-
const { bum, scope, job, subTree, um, m, a } = instance
2342+
const { bum, scope, effect, subTree, um, m, a } = instance
23472343
invalidateMount(m)
23482344
invalidateMount(a)
23492345

@@ -2364,9 +2360,9 @@ function baseCreateRenderer(
23642360

23652361
// job may be null if a component is unmounted before its async
23662362
// setup has resolved.
2367-
if (job) {
2363+
if (effect) {
23682364
// so that scheduler will no longer invoke it
2369-
job.flags! |= SchedulerJobFlags.DISPOSED
2365+
effect.stop()
23702366
unmount(subTree, instance, parentSuspense, doRemove)
23712367
}
23722368
// unmounted hook
@@ -2549,16 +2545,14 @@ function resolveChildrenNamespace(
25492545
}
25502546

25512547
function toggleRecurse(
2552-
{ effect, job, vapor }: ComponentInternalInstance,
2548+
{ effect, vapor }: ComponentInternalInstance,
25532549
allowed: boolean,
25542550
) {
25552551
if (!vapor) {
25562552
if (allowed) {
25572553
effect.flags |= EffectFlags.ALLOW_RECURSE
2558-
job.flags! |= SchedulerJobFlags.ALLOW_RECURSE
25592554
} else {
25602555
effect.flags &= ~EffectFlags.ALLOW_RECURSE
2561-
job.flags! &= ~SchedulerJobFlags.ALLOW_RECURSE
25622556
}
25632557
}
25642558
}

packages/runtime-vapor/src/renderEffect.ts

+10-26
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { EffectFlags, type EffectScope, ReactiveEffect } from '@vue/reactivity'
1+
import { type EffectScope, ReactiveEffect } from '@vue/reactivity'
22
import {
3-
type SchedulerJob,
43
currentInstance,
5-
queueJob,
64
queuePostFlushCb,
75
setCurrentInstance,
86
startMeasure,
@@ -13,33 +11,28 @@ import { invokeArrayFns } from '@vue/shared'
1311

1412
class RenderEffect extends ReactiveEffect {
1513
i: VaporComponentInstance | null
16-
job: SchedulerJob
1714

1815
constructor(public render: () => void) {
1916
super()
20-
const instance = currentInstance as VaporComponentInstance | null
21-
if (__DEV__ && !__TEST__ && !this.subs && !isVaporComponent(instance)) {
22-
warn('renderEffect called without active EffectScope or Vapor instance.')
23-
}
24-
25-
const job: SchedulerJob = super.scheduler.bind(this)
17+
this.flags |= 1 << 10 // SubscriberFlags.Job
18+
const instance = (this.i = currentInstance as VaporComponentInstance | null)
2619

27-
if (instance) {
28-
if (__DEV__) {
20+
if (__DEV__) {
21+
if (!__TEST__ && !this.subs && !isVaporComponent(instance)) {
22+
warn(
23+
'renderEffect called without active EffectScope or Vapor instance.',
24+
)
25+
}
26+
if (instance) {
2927
this.onTrack = instance.rtc
3028
? e => invokeArrayFns(instance.rtc!, e)
3129
: void 0
3230
this.onTrigger = instance.rtg
3331
? e => invokeArrayFns(instance.rtg!, e)
3432
: void 0
3533
}
36-
job.i = instance
37-
job.id = instance.uid
3834
}
3935

40-
this.job = job
41-
this.i = instance
42-
4336
// TODO recurse handling
4437
}
4538

@@ -72,15 +65,6 @@ class RenderEffect extends ReactiveEffect {
7265
startMeasure(instance, `renderEffect`)
7366
}
7467
}
75-
76-
notify(): void {
77-
const flags = this.flags
78-
if (!(flags & EffectFlags.PAUSED)) {
79-
queueJob(this.job)
80-
} else {
81-
this.flags = flags | EffectFlags.NOTIFIED
82-
}
83-
}
8468
}
8569

8670
class RenderEffect_NoLifecycle extends RenderEffect {

0 commit comments

Comments
 (0)