diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 6811f1029..112af2826 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -4,7 +4,8 @@ import { effect, ref, WritableComputedRef, - isReadonly + isReadonly, + setComputedScheduler } from '../src' describe('reactivity/computed', () => { @@ -198,4 +199,58 @@ describe('reactivity/computed', () => { x.effect.stop() expect(x.value).toBe(1) }) + + describe('with scheduler', () => { + const p = Promise.resolve() + const defer = (fn?: any) => (fn ? p.then(fn) : p) + beforeEach(() => { + setComputedScheduler(defer) + }) + + afterEach(() => { + setComputedScheduler(undefined) + }) + + test('should only trigger once on multiple mutations', async () => { + const src = ref(0) + const c = computed(() => src.value) + const spy = jest.fn() + effect(() => { + spy(c.value) + }) + expect(spy).toHaveBeenCalledTimes(1) + src.value = 1 + src.value = 2 + src.value = 3 + // not called yet + expect(spy).toHaveBeenCalledTimes(1) + await defer() + // should only trigger once + expect(spy).toHaveBeenCalledTimes(2) + expect(spy).toHaveBeenCalledWith(c.value) + }) + + test('should not trigger if value did not change', async () => { + const src = ref(0) + const c = computed(() => src.value % 2) + const spy = jest.fn() + effect(() => { + spy(c.value) + }) + expect(spy).toHaveBeenCalledTimes(1) + src.value = 1 + src.value = 2 + + await defer() + // should not trigger + expect(spy).toHaveBeenCalledTimes(1) + + src.value = 3 + src.value = 4 + src.value = 5 + await defer() + // should trigger because latest value changes + expect(spy).toHaveBeenCalledTimes(2) + }) + }) }) diff --git a/packages/runtime-core/__tests__/rendererComponent.spec.ts b/packages/runtime-core/__tests__/rendererComponent.spec.ts index 4d20ca80b..889ec0513 100644 --- a/packages/runtime-core/__tests__/rendererComponent.spec.ts +++ b/packages/runtime-core/__tests__/rendererComponent.spec.ts @@ -10,7 +10,8 @@ import { inject, Ref, watch, - SetupContext + SetupContext, + computed } from '@vue/runtime-test' describe('renderer: component', () => { @@ -321,4 +322,36 @@ describe('renderer: component', () => { expect(serializeInner(root)).toBe(``) expect(ids).toEqual([ids[0], ids[0] + 1, ids[0] + 2]) }) + + test('computed that did not change should not trigger re-render', async () => { + const src = ref(0) + const c = computed(() => src.value % 2) + const spy = jest.fn() + const App = { + render() { + spy() + return c.value + } + } + + const root = nodeOps.createElement('div') + render(h(App), root) + expect(serializeInner(root)).toBe(`0`) + expect(spy).toHaveBeenCalledTimes(1) + + // verify it updates + src.value = 1 + src.value = 2 + src.value = 3 + await nextTick() + expect(serializeInner(root)).toBe(`1`) + expect(spy).toHaveBeenCalledTimes(2) // should only update once + + // verify it updates + src.value = 4 + src.value = 5 + await nextTick() + expect(serializeInner(root)).toBe(`1`) + expect(spy).toHaveBeenCalledTimes(2) // should not need to update + }) })