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
This commit is contained in:
Evan You 2020-04-22 15:11:01 -04:00
parent 7d858a9001
commit b0d4df9743
2 changed files with 40 additions and 15 deletions

View File

@ -22,11 +22,19 @@ describe('reactivity/ref', () => {
it('should be reactive', () => { it('should be reactive', () => {
const a = ref(1) const a = ref(1)
let dummy let dummy
let calls = 0
effect(() => { effect(() => {
calls++
dummy = a.value dummy = a.value
}) })
expect(calls).toBe(1)
expect(dummy).toBe(1) expect(dummy).toBe(1)
a.value = 2 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) expect(dummy).toBe(2)
}) })
@ -174,6 +182,22 @@ describe('reactivity/ref', () => {
expect(dummy).toBe(2) 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', () => { test('isRef', () => {
expect(isRef(ref(1))).toBe(true) expect(isRef(ref(1))).toBe(true)
expect(isRef(computed(() => 1))).toBe(true) expect(isRef(computed(() => 1))).toBe(true)

View File

@ -1,7 +1,7 @@
import { track, trigger } from './effect' import { track, trigger } from './effect'
import { TrackOpTypes, TriggerOpTypes } from './operations' import { TrackOpTypes, TriggerOpTypes } from './operations'
import { isObject } from '@vue/shared' import { isObject, hasChanged } from '@vue/shared'
import { reactive, isProxy } from './reactive' import { reactive, isProxy, toRaw } from './reactive'
import { ComputedRef } from './computed' import { ComputedRef } from './computed'
import { CollectionTypes } from './collectionHandlers' import { CollectionTypes } from './collectionHandlers'
@ -43,13 +43,11 @@ export function shallowRef(value?: unknown) {
return createRef(value, true) return createRef(value, true)
} }
function createRef(value: unknown, shallow = false) { function createRef(rawValue: unknown, shallow = false) {
if (isRef(value)) { if (isRef(rawValue)) {
return value return rawValue
}
if (!shallow) {
value = convert(value)
} }
let value = shallow ? rawValue : convert(rawValue)
const r = { const r = {
_isRef: true, _isRef: true,
get value() { get value() {
@ -57,13 +55,16 @@ function createRef(value: unknown, shallow = false) {
return value return value
}, },
set value(newVal) { set value(newVal) {
value = shallow ? newVal : convert(newVal) if (shallow || hasChanged(toRaw(newVal), rawValue)) {
trigger( rawValue = newVal
r, value = shallow ? newVal : convert(newVal)
TriggerOpTypes.SET, trigger(
'value', r,
__DEV__ ? { newValue: newVal } : void 0 TriggerOpTypes.SET,
) 'value',
__DEV__ ? { newValue: newVal } : void 0
)
}
} }
} }
return r return r