From b0d4df974339a570fd30263797cf948619e1f57b Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 22 Apr 2020 15:11:01 -0400 Subject: [PATCH] perf(reactivity): ref should not trigger if value did not change Note: shallowRef will always trigger on assignment because it does not account for deep mutations close #1012 --- packages/reactivity/__tests__/ref.spec.ts | 24 ++++++++++++++++++ packages/reactivity/src/ref.ts | 31 ++++++++++++----------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/packages/reactivity/__tests__/ref.spec.ts b/packages/reactivity/__tests__/ref.spec.ts index 7885ff7d0c8..f6fc7f2319a 100644 --- a/packages/reactivity/__tests__/ref.spec.ts +++ b/packages/reactivity/__tests__/ref.spec.ts @@ -22,11 +22,19 @@ describe('reactivity/ref', () => { it('should be reactive', () => { const a = ref(1) let dummy + let calls = 0 effect(() => { + calls++ dummy = a.value }) + expect(calls).toBe(1) expect(dummy).toBe(1) a.value = 2 + expect(calls).toBe(2) + expect(dummy).toBe(2) + // same value should not trigger + a.value = 2 + expect(calls).toBe(2) expect(dummy).toBe(2) }) @@ -174,6 +182,22 @@ describe('reactivity/ref', () => { expect(dummy).toBe(2) }) + test('shallowRef force trigger', () => { + const sref = shallowRef({ a: 1 }) + let dummy + effect(() => { + dummy = sref.value.a + }) + expect(dummy).toBe(1) + + sref.value.a = 2 + expect(dummy).toBe(1) // should not trigger yet + + // force trigger + sref.value = sref.value + expect(dummy).toBe(2) + }) + test('isRef', () => { expect(isRef(ref(1))).toBe(true) expect(isRef(computed(() => 1))).toBe(true) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 79c2dc52c2a..59badad4d56 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -1,7 +1,7 @@ import { track, trigger } from './effect' import { TrackOpTypes, TriggerOpTypes } from './operations' -import { isObject } from '@vue/shared' -import { reactive, isProxy } from './reactive' +import { isObject, hasChanged } from '@vue/shared' +import { reactive, isProxy, toRaw } from './reactive' import { ComputedRef } from './computed' import { CollectionTypes } from './collectionHandlers' @@ -43,13 +43,11 @@ export function shallowRef(value?: unknown) { return createRef(value, true) } -function createRef(value: unknown, shallow = false) { - if (isRef(value)) { - return value - } - if (!shallow) { - value = convert(value) +function createRef(rawValue: unknown, shallow = false) { + if (isRef(rawValue)) { + return rawValue } + let value = shallow ? rawValue : convert(rawValue) const r = { _isRef: true, get value() { @@ -57,13 +55,16 @@ function createRef(value: unknown, shallow = false) { return value }, set value(newVal) { - value = shallow ? newVal : convert(newVal) - trigger( - r, - TriggerOpTypes.SET, - 'value', - __DEV__ ? { newValue: newVal } : void 0 - ) + if (shallow || hasChanged(toRaw(newVal), rawValue)) { + rawValue = newVal + value = shallow ? newVal : convert(newVal) + trigger( + r, + TriggerOpTypes.SET, + 'value', + __DEV__ ? { newValue: newVal } : void 0 + ) + } } } return r