Skip to content

Commit b0d4df9

Browse files
committed
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
1 parent 7d858a9 commit b0d4df9

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

packages/reactivity/__tests__/ref.spec.ts

+24
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,19 @@ describe('reactivity/ref', () => {
2222
it('should be reactive', () => {
2323
const a = ref(1)
2424
let dummy
25+
let calls = 0
2526
effect(() => {
27+
calls++
2628
dummy = a.value
2729
})
30+
expect(calls).toBe(1)
2831
expect(dummy).toBe(1)
2932
a.value = 2
33+
expect(calls).toBe(2)
34+
expect(dummy).toBe(2)
35+
// same value should not trigger
36+
a.value = 2
37+
expect(calls).toBe(2)
3038
expect(dummy).toBe(2)
3139
})
3240

@@ -174,6 +182,22 @@ describe('reactivity/ref', () => {
174182
expect(dummy).toBe(2)
175183
})
176184

185+
test('shallowRef force trigger', () => {
186+
const sref = shallowRef({ a: 1 })
187+
let dummy
188+
effect(() => {
189+
dummy = sref.value.a
190+
})
191+
expect(dummy).toBe(1)
192+
193+
sref.value.a = 2
194+
expect(dummy).toBe(1) // should not trigger yet
195+
196+
// force trigger
197+
sref.value = sref.value
198+
expect(dummy).toBe(2)
199+
})
200+
177201
test('isRef', () => {
178202
expect(isRef(ref(1))).toBe(true)
179203
expect(isRef(computed(() => 1))).toBe(true)

packages/reactivity/src/ref.ts

+16-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { track, trigger } from './effect'
22
import { TrackOpTypes, TriggerOpTypes } from './operations'
3-
import { isObject } from '@vue/shared'
4-
import { reactive, isProxy } from './reactive'
3+
import { isObject, hasChanged } from '@vue/shared'
4+
import { reactive, isProxy, toRaw } from './reactive'
55
import { ComputedRef } from './computed'
66
import { CollectionTypes } from './collectionHandlers'
77

@@ -43,27 +43,28 @@ export function shallowRef(value?: unknown) {
4343
return createRef(value, true)
4444
}
4545

46-
function createRef(value: unknown, shallow = false) {
47-
if (isRef(value)) {
48-
return value
49-
}
50-
if (!shallow) {
51-
value = convert(value)
46+
function createRef(rawValue: unknown, shallow = false) {
47+
if (isRef(rawValue)) {
48+
return rawValue
5249
}
50+
let value = shallow ? rawValue : convert(rawValue)
5351
const r = {
5452
_isRef: true,
5553
get value() {
5654
track(r, TrackOpTypes.GET, 'value')
5755
return value
5856
},
5957
set value(newVal) {
60-
value = shallow ? newVal : convert(newVal)
61-
trigger(
62-
r,
63-
TriggerOpTypes.SET,
64-
'value',
65-
__DEV__ ? { newValue: newVal } : void 0
66-
)
58+
if (shallow || hasChanged(toRaw(newVal), rawValue)) {
59+
rawValue = newVal
60+
value = shallow ? newVal : convert(newVal)
61+
trigger(
62+
r,
63+
TriggerOpTypes.SET,
64+
'value',
65+
__DEV__ ? { newValue: newVal } : void 0
66+
)
67+
}
6768
}
6869
}
6970
return r

0 commit comments

Comments
 (0)