Skip to content

Commit b6fc1f7

Browse files
authored
fix(shallowReadonly): watch should work for ref/reactive with shallowReadonly (#714)
1 parent 620d09b commit b6fc1f7

File tree

4 files changed

+76
-9
lines changed

4 files changed

+76
-9
lines changed

src/reactivity/reactive.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export function defineAccessControl(target: AnyObject, key: any, val?: any) {
101101
})
102102
}
103103

104-
function observe<T>(obj: T): T {
104+
export function observe<T>(obj: T): T {
105105
const Vue = getRegisteredVueOrDefault()
106106
let observed: T
107107
if (Vue.observable) {

src/reactivity/readonly.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { reactive, Ref, UnwrapRef } from '.'
22
import { isArray, isPlainObject, warn } from '../utils'
33
import { readonlySet } from '../utils/sets'
4-
import { isRef } from './ref'
4+
import { isReactive, observe } from './reactive'
5+
import { isRef, RefImpl } from './ref'
56

67
export function isReadonly(obj: any): boolean {
78
return readonlySet.has(obj)
@@ -55,17 +56,20 @@ export function shallowReadonly(obj: any): any {
5556
return obj
5657
}
5758

58-
const readonlyObj = {}
59-
59+
const readonlyObj = isRef(obj)
60+
? new RefImpl({} as any)
61+
: isReactive(obj)
62+
? observe({})
63+
: {}
6064
const source = reactive({})
6165
const ob = (source as any).__ob__
6266

6367
for (const key of Object.keys(obj)) {
6468
let val = obj[key]
6569
let getter: (() => any) | undefined
6670
const property = Object.getOwnPropertyDescriptor(obj, key)
67-
if (property && !isRef(obj)) {
68-
if (property.configurable === false) {
71+
if (property) {
72+
if (property.configurable === false && !isRef(obj)) {
6973
continue
7074
}
7175
getter = property.get

src/reactivity/ref.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ interface RefOption<T> {
7171
get(): T
7272
set?(x: T): void
7373
}
74-
class RefImpl<T> implements Ref<T> {
74+
export class RefImpl<T> implements Ref<T> {
7575
readonly [_refBrand]!: true
7676
public value!: T
7777
constructor({ get, set }: RefOption<T>) {

test/v3/reactivity/readonly.spec.ts

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { mockWarn } from '../../helpers/mockWarn'
2-
import { shallowReadonly, isReactive, ref, reactive } from '../../../src'
2+
import {
3+
shallowReadonly,
4+
isReactive,
5+
ref,
6+
reactive,
7+
watch,
8+
nextTick,
9+
} from '../../../src'
310

411
const Vue = require('vue/dist/vue.common.js')
512

@@ -391,7 +398,7 @@ describe('reactivity/readonly', () => {
391398
countReadonly.number++
392399
// @ts-expect-error
393400
countRefReadonly.value++
394-
}, 2000)
401+
}, 1000)
395402
return {
396403
count,
397404
countRef,
@@ -401,5 +408,61 @@ describe('reactivity/readonly', () => {
401408

402409
expect(vm.$el.textContent).toBe(`{\n "number": 0\n} 0`)
403410
})
411+
412+
// #712
413+
test('watch should work for ref with shallowReadonly', async () => {
414+
const cb = jest.fn()
415+
const vm = new Vue({
416+
template: '<div>{{ countRef }}</div>',
417+
setup() {
418+
const countRef = ref(0)
419+
const countRefReadonly = shallowReadonly(countRef)
420+
watch(countRefReadonly, cb, { deep: true })
421+
return {
422+
countRef,
423+
countRefReadonly,
424+
}
425+
},
426+
}).$mount()
427+
428+
vm.countRef++
429+
await nextTick()
430+
expect(cb).toHaveBeenCalled()
431+
432+
vm.countRefReadonly++
433+
await nextTick()
434+
expect(
435+
`Set operation on key "value" failed: target is readonly.`
436+
).toHaveBeenWarned()
437+
expect(vm.$el.textContent).toBe(`1`)
438+
})
439+
440+
// #712
441+
test('watch should work for reactive with shallowReadonly', async () => {
442+
const cb = jest.fn()
443+
const vm = new Vue({
444+
template: '<div>{{ count.number }}</div>',
445+
setup() {
446+
const count = reactive({ number: 0 })
447+
const countReadonly = shallowReadonly(count)
448+
watch(countReadonly, cb, { deep: true })
449+
return {
450+
count,
451+
countReadonly,
452+
}
453+
},
454+
}).$mount()
455+
456+
vm.count.number++
457+
await nextTick()
458+
expect(cb).toHaveBeenCalled()
459+
460+
vm.countReadonly.number++
461+
await nextTick()
462+
expect(
463+
`Set operation on key "number" failed: target is readonly.`
464+
).toHaveBeenWarned()
465+
expect(vm.$el.textContent).toBe(`1`)
466+
})
404467
})
405468
})

0 commit comments

Comments
 (0)