Skip to content

Commit 33c2fbf

Browse files
committed
fix(reactivity): revert computed scheduler change
fix #4157
1 parent bc7f976 commit 33c2fbf

File tree

5 files changed

+10
-304
lines changed

5 files changed

+10
-304
lines changed

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

-215
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
ref,
66
WritableComputedRef,
77
isReadonly,
8-
setComputedScheduler,
98
DebuggerEvent,
109
toRaw,
1110
TrackOpTypes,
@@ -273,218 +272,4 @@ describe('reactivity/computed', () => {
273272
oldValue: 2
274273
})
275274
})
276-
277-
describe('with scheduler', () => {
278-
// a simple scheduler similar to the main Vue scheduler
279-
const tick = Promise.resolve()
280-
const queue: any[] = []
281-
let queued = false
282-
283-
const schedule = (fn: any) => {
284-
queue.push(fn)
285-
if (!queued) {
286-
queued = true
287-
tick.then(flush)
288-
}
289-
}
290-
291-
const flush = () => {
292-
for (let i = 0; i < queue.length; i++) {
293-
queue[i]()
294-
}
295-
queue.length = 0
296-
queued = false
297-
}
298-
299-
beforeEach(() => {
300-
setComputedScheduler(schedule)
301-
})
302-
303-
afterEach(() => {
304-
setComputedScheduler(undefined)
305-
})
306-
307-
test('should only trigger once on multiple mutations', async () => {
308-
const src = ref(0)
309-
const c = computed(() => src.value)
310-
const spy = jest.fn()
311-
effect(() => {
312-
spy(c.value)
313-
})
314-
expect(spy).toHaveBeenCalledTimes(1)
315-
src.value = 1
316-
src.value = 2
317-
src.value = 3
318-
// not called yet
319-
expect(spy).toHaveBeenCalledTimes(1)
320-
await tick
321-
// should only trigger once
322-
expect(spy).toHaveBeenCalledTimes(2)
323-
expect(spy).toHaveBeenCalledWith(c.value)
324-
})
325-
326-
test('should not trigger if value did not change', async () => {
327-
const src = ref(0)
328-
const c = computed(() => src.value % 2)
329-
const spy = jest.fn()
330-
effect(() => {
331-
spy(c.value)
332-
})
333-
expect(spy).toHaveBeenCalledTimes(1)
334-
src.value = 1
335-
src.value = 2
336-
337-
await tick
338-
// should not trigger
339-
expect(spy).toHaveBeenCalledTimes(1)
340-
341-
src.value = 3
342-
src.value = 4
343-
src.value = 5
344-
await tick
345-
// should trigger because latest value changes
346-
expect(spy).toHaveBeenCalledTimes(2)
347-
})
348-
349-
test('chained computed trigger', async () => {
350-
const effectSpy = jest.fn()
351-
const c1Spy = jest.fn()
352-
const c2Spy = jest.fn()
353-
354-
const src = ref(0)
355-
const c1 = computed(() => {
356-
c1Spy()
357-
return src.value % 2
358-
})
359-
const c2 = computed(() => {
360-
c2Spy()
361-
return c1.value + 1
362-
})
363-
364-
effect(() => {
365-
effectSpy(c2.value)
366-
})
367-
368-
expect(c1Spy).toHaveBeenCalledTimes(1)
369-
expect(c2Spy).toHaveBeenCalledTimes(1)
370-
expect(effectSpy).toHaveBeenCalledTimes(1)
371-
372-
src.value = 1
373-
await tick
374-
expect(c1Spy).toHaveBeenCalledTimes(2)
375-
expect(c2Spy).toHaveBeenCalledTimes(2)
376-
expect(effectSpy).toHaveBeenCalledTimes(2)
377-
})
378-
379-
test('chained computed avoid re-compute', async () => {
380-
const effectSpy = jest.fn()
381-
const c1Spy = jest.fn()
382-
const c2Spy = jest.fn()
383-
384-
const src = ref(0)
385-
const c1 = computed(() => {
386-
c1Spy()
387-
return src.value % 2
388-
})
389-
const c2 = computed(() => {
390-
c2Spy()
391-
return c1.value + 1
392-
})
393-
394-
effect(() => {
395-
effectSpy(c2.value)
396-
})
397-
398-
expect(effectSpy).toHaveBeenCalledTimes(1)
399-
src.value = 2
400-
src.value = 4
401-
src.value = 6
402-
await tick
403-
// c1 should re-compute once.
404-
expect(c1Spy).toHaveBeenCalledTimes(2)
405-
// c2 should not have to re-compute because c1 did not change.
406-
expect(c2Spy).toHaveBeenCalledTimes(1)
407-
// effect should not trigger because c2 did not change.
408-
expect(effectSpy).toHaveBeenCalledTimes(1)
409-
})
410-
411-
test('chained computed value invalidation', async () => {
412-
const effectSpy = jest.fn()
413-
const c1Spy = jest.fn()
414-
const c2Spy = jest.fn()
415-
416-
const src = ref(0)
417-
const c1 = computed(() => {
418-
c1Spy()
419-
return src.value % 2
420-
})
421-
const c2 = computed(() => {
422-
c2Spy()
423-
return c1.value + 1
424-
})
425-
426-
effect(() => {
427-
effectSpy(c2.value)
428-
})
429-
430-
expect(effectSpy).toHaveBeenCalledTimes(1)
431-
expect(effectSpy).toHaveBeenCalledWith(1)
432-
expect(c2.value).toBe(1)
433-
434-
expect(c1Spy).toHaveBeenCalledTimes(1)
435-
expect(c2Spy).toHaveBeenCalledTimes(1)
436-
437-
src.value = 1
438-
// value should be available sync
439-
expect(c2.value).toBe(2)
440-
expect(c2Spy).toHaveBeenCalledTimes(2)
441-
})
442-
443-
test('sync access of invalidated chained computed should not prevent final effect from running', async () => {
444-
const effectSpy = jest.fn()
445-
const c1Spy = jest.fn()
446-
const c2Spy = jest.fn()
447-
448-
const src = ref(0)
449-
const c1 = computed(() => {
450-
c1Spy()
451-
return src.value % 2
452-
})
453-
const c2 = computed(() => {
454-
c2Spy()
455-
return c1.value + 1
456-
})
457-
458-
effect(() => {
459-
effectSpy(c2.value)
460-
})
461-
expect(effectSpy).toHaveBeenCalledTimes(1)
462-
463-
src.value = 1
464-
// sync access c2
465-
c2.value
466-
await tick
467-
expect(effectSpy).toHaveBeenCalledTimes(2)
468-
})
469-
470-
test('should not compute if deactivated before scheduler is called', async () => {
471-
const c1Spy = jest.fn()
472-
const src = ref(0)
473-
const c1 = computed(() => {
474-
c1Spy()
475-
return src.value % 2
476-
})
477-
effect(() => c1.value)
478-
expect(c1Spy).toHaveBeenCalledTimes(1)
479-
480-
// schedule stop
481-
schedule(() => {
482-
c1.effect.stop()
483-
})
484-
// trigger
485-
src.value++
486-
await tick
487-
expect(c1Spy).toHaveBeenCalledTimes(1)
488-
})
489-
})
490275
})

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

+9-50
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,6 @@ export interface WritableComputedOptions<T> {
2020
set: ComputedSetter<T>
2121
}
2222

23-
type ComputedScheduler = (fn: () => void) => void
24-
let scheduler: ComputedScheduler | undefined
25-
26-
/**
27-
* Set a scheduler for deferring computed computations
28-
*/
29-
export const setComputedScheduler = (s: ComputedScheduler | undefined) => {
30-
scheduler = s
31-
}
32-
3323
class ComputedRefImpl<T> {
3424
public dep?: Dep = undefined
3525

@@ -45,55 +35,24 @@ class ComputedRefImpl<T> {
4535
private readonly _setter: ComputedSetter<T>,
4636
isReadonly: boolean
4737
) {
48-
let compareTarget: any
49-
let hasCompareTarget = false
50-
let scheduled = false
51-
this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => {
52-
if (scheduler && this.dep) {
53-
if (computedTrigger) {
54-
compareTarget = this._value
55-
hasCompareTarget = true
56-
} else if (!scheduled) {
57-
const valueToCompare = hasCompareTarget ? compareTarget : this._value
58-
scheduled = true
59-
hasCompareTarget = false
60-
scheduler(() => {
61-
if (this.effect.active && this._get() !== valueToCompare) {
62-
triggerRefValue(this)
63-
}
64-
scheduled = false
65-
})
66-
}
67-
// chained upstream computeds are notified synchronously to ensure
68-
// value invalidation in case of sync access; normal effects are
69-
// deferred to be triggered in scheduler.
70-
for (const e of this.dep) {
71-
if (e.computed) {
72-
e.scheduler!(true /* computedTrigger */)
73-
}
74-
}
75-
}
38+
this.effect = new ReactiveEffect(getter, () => {
7639
if (!this._dirty) {
7740
this._dirty = true
78-
if (!scheduler) triggerRefValue(this)
41+
triggerRefValue(this)
7942
}
8043
})
81-
this.effect.computed = true
8244
this[ReactiveFlags.IS_READONLY] = isReadonly
8345
}
8446

85-
private _get() {
86-
if (this._dirty) {
87-
this._dirty = false
88-
return (this._value = this.effect.run()!)
89-
}
90-
return this._value
91-
}
92-
9347
get value() {
94-
trackRefValue(this)
9548
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
96-
return toRaw(this)._get()
49+
const self = toRaw(this)
50+
trackRefValue(self)
51+
if (self._dirty) {
52+
self._dirty = false
53+
self._value = self.effect.run()!
54+
}
55+
return self._value
9756
}
9857

9958
set value(newValue: T) {

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

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export {
3030
} from './reactive'
3131
export {
3232
computed,
33-
setComputedScheduler,
3433
ComputedRef,
3534
WritableComputedRef,
3635
WritableComputedOptions,

Diff for: packages/runtime-core/__tests__/rendererComponent.spec.ts

+1-34
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import {
1010
inject,
1111
Ref,
1212
watch,
13-
SetupContext,
14-
computed
13+
SetupContext
1514
} from '@vue/runtime-test'
1615

1716
describe('renderer: component', () => {
@@ -325,36 +324,4 @@ describe('renderer: component', () => {
325324
expect(serializeInner(root)).toBe(``)
326325
expect(ids).toEqual([ids[0], ids[0] + 1, ids[0] + 2])
327326
})
328-
329-
test('computed that did not change should not trigger re-render', async () => {
330-
const src = ref(0)
331-
const c = computed(() => src.value % 2)
332-
const spy = jest.fn()
333-
const App = {
334-
render() {
335-
spy()
336-
return c.value
337-
}
338-
}
339-
340-
const root = nodeOps.createElement('div')
341-
render(h(App), root)
342-
expect(serializeInner(root)).toBe(`0`)
343-
expect(spy).toHaveBeenCalledTimes(1)
344-
345-
// verify it updates
346-
src.value = 1
347-
src.value = 2
348-
src.value = 3
349-
await nextTick()
350-
expect(serializeInner(root)).toBe(`1`)
351-
expect(spy).toHaveBeenCalledTimes(2) // should only update once
352-
353-
// verify it updates
354-
src.value = 4
355-
src.value = 5
356-
await nextTick()
357-
expect(serializeInner(root)).toBe(`1`)
358-
expect(spy).toHaveBeenCalledTimes(2) // should not need to update
359-
})
360327
})

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

-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ import { ErrorCodes, callWithErrorHandling } from './errorHandling'
22
import { isArray } from '@vue/shared'
33
import { ComponentInternalInstance, getComponentName } from './component'
44
import { warn } from './warning'
5-
import { setComputedScheduler } from '@vue/reactivity'
6-
7-
// set scheduler for computed
8-
setComputedScheduler(queueJob)
95

106
export interface SchedulerJob extends Function {
117
id?: number

0 commit comments

Comments
 (0)