diff --git a/src/reactivity/unwrap.ts b/src/reactivity/unwrap.ts
index a59fb29d..e7f591fe 100644
--- a/src/reactivity/unwrap.ts
+++ b/src/reactivity/unwrap.ts
@@ -2,19 +2,24 @@ import { isRef } from './ref'
import { proxy, isFunction, isPlainObject, isArray } from '../utils'
import { isReactive } from './reactive'
-export function unwrapRefProxy(value: any) {
+export function unwrapRefProxy(value: any, map = new WeakMap()) {
+ if (map.has(value)) {
+ return map.get(value)
+ }
+
if (
isFunction(value) ||
- isRef(value) ||
isArray(value) ||
isReactive(value) ||
!isPlainObject(value) ||
- !Object.isExtensible(value)
+ !Object.isExtensible(value) ||
+ isRef(value)
) {
return value
}
const obj: any = {}
+ map.set(value, obj)
// copy symbols over
Object.getOwnPropertySymbols(value).forEach(
@@ -30,7 +35,7 @@ export function unwrapRefProxy(value: any) {
proxy(obj, k, { get, set })
} else {
- obj[k] = unwrapRefProxy(r)
+ obj[k] = unwrapRefProxy(r, map)
}
}
diff --git a/test/setup.spec.js b/test/setup.spec.js
index 98e50fcd..99e30eff 100644
--- a/test/setup.spec.js
+++ b/test/setup.spec.js
@@ -467,105 +467,161 @@ describe('setup', () => {
.then(done)
})
- it('should unwrap on the template', () => {
- const vm = new Vue({
- setup() {
- const r = ref('r')
- const nested = {
- a: ref('a'),
- aa: {
- b: ref('aa'),
- bb: {
- cc: ref('aa'),
- c: 'aa',
- },
- },
+ describe('setup unwrap', () => {
+ test('ref', () => {
+ const vm = new Vue({
+ setup() {
+ const r = ref('r')
+
+ const refList = ref([ref('1'), ref('2'), ref('3')])
+ const list = [ref('a'), ref('b')]
+
+ return {
+ r,
+ refList,
+ list,
+ }
+ },
+ template: `
+
{{r}}
+
{{list}}
+
{{refList}}
+
`,
+ }).$mount()
+
+ expect(vm.$el.querySelector('#r').textContent).toBe('r')
+
+ // shouldn't unwrap arrays
+ expect(
+ JSON.parse(vm.$el.querySelector('#list').textContent)
+ ).toMatchObject([{ value: 'a' }, { value: 'b' }])
+ expect(
+ JSON.parse(vm.$el.querySelector('#refList').textContent)
+ ).toMatchObject([{ value: '1' }, { value: '2' }, { value: '3' }])
+ })
- aaa: reactive({
- b: ref('aaa'),
- bb: {
- c: ref('aaa'),
- cc: 'aaa',
+ test('nested', () => {
+ const vm = new Vue({
+ setup() {
+ const nested = {
+ a: ref('a'),
+ aa: {
+ b: ref('aa'),
+ bb: {
+ cc: ref('aa'),
+ c: 'aa',
+ },
},
- }),
-
- aaaa: {
- b: [1],
- bb: ref([1]),
- bbb: reactive({
- c: [1],
- cc: ref([1]),
+
+ aaa: reactive({
+ b: ref('aaa'),
+ bb: {
+ c: ref('aaa'),
+ cc: 'aaa',
+ },
}),
- bbbb: [ref(1)],
- },
- }
- const refList = ref([ref('1'), ref('2'), ref('3')])
- const list = [ref('a'), ref('b')]
+ aaaa: {
+ b: [1],
+ bb: ref([1]),
+ bbb: reactive({
+ c: [1],
+ cc: ref([1]),
+ }),
+ bbbb: [ref(1)],
+ },
+ }
- return {
- r,
- nested,
- refList,
- list,
- }
- },
- template: `
-
{{r}}
-
{{nested.a}}
-
{{list}}
-
{{refList}}
-
-
{{ nested.aa.b }}
-
{{ nested.aa.bb.c }}
-
{{ nested.aa.bb.cc }}
-
-
{{ nested.aaa.b }}
-
{{ nested.aaa.bb.c }}
-
{{ nested.aaa.bb.cc }}
-
-
{{ nested.aaaa.b }}
-
{{ nested.aaaa.bb }}
-
{{ nested.aaaa.bbb.c }}
-
{{ nested.aaaa.bbb.cc }}
-
{{ nested.aaaa.bbbb }}
-
`,
- }).$mount()
+ return {
+ nested,
+ }
+ },
+ template: `
+
{{nested.a}}
+
+
{{ nested.aa.b }}
+
{{ nested.aa.bb.c }}
+
{{ nested.aa.bb.cc }}
+
+
{{ nested.aaa.b }}
+
{{ nested.aaa.bb.c }}
+
{{ nested.aaa.bb.cc }}
+
+
{{ nested.aaaa.b }}
+
{{ nested.aaaa.bb }}
+
{{ nested.aaaa.bbb.c }}
+
{{ nested.aaaa.bbb.cc }}
+
{{ nested.aaaa.bbbb }}
+
`,
+ }).$mount()
- expect(vm.$el.querySelector('#r').textContent).toBe('r')
- expect(vm.$el.querySelector('#nested').textContent).toBe('a')
+ expect(vm.$el.querySelector('#nested').textContent).toBe('a')
- // shouldn't unwrap arrays
- expect(
- JSON.parse(vm.$el.querySelector('#list').textContent)
- ).toMatchObject([{ value: 'a' }, { value: 'b' }])
- expect(
- JSON.parse(vm.$el.querySelector('#refList').textContent)
- ).toMatchObject([{ value: '1' }, { value: '2' }, { value: '3' }])
+ expect(vm.$el.querySelector('#nested_aa_b').textContent).toBe('aa')
+ expect(vm.$el.querySelector('#nested_aa_bb_c').textContent).toBe('aa')
+ expect(vm.$el.querySelector('#nested_aa_bb_cc').textContent).toBe('aa')
- expect(vm.$el.querySelector('#nested_aa_b').textContent).toBe('aa')
- expect(vm.$el.querySelector('#nested_aa_bb_c').textContent).toBe('aa')
- expect(vm.$el.querySelector('#nested_aa_bb_cc').textContent).toBe('aa')
+ expect(vm.$el.querySelector('#nested_aaa_b').textContent).toBe('aaa')
+ expect(vm.$el.querySelector('#nested_aaa_bb_c').textContent).toBe('aaa')
+ expect(vm.$el.querySelector('#nested_aaa_bb_cc').textContent).toBe('aaa')
+ })
- expect(vm.$el.querySelector('#nested_aaa_b').textContent).toBe('aaa')
- expect(vm.$el.querySelector('#nested_aaa_bb_c').textContent).toBe('aaa')
- expect(vm.$el.querySelector('#nested_aaa_bb_cc').textContent).toBe('aaa')
+ it('recursive', () => {
+ const vm = new Vue({
+ setup() {
+ const b = {
+ c: 'c',
+ }
- expect(
- JSON.parse(vm.$el.querySelector('#nested_aaaa_b').textContent)
- ).toMatchObject([1])
- expect(
- JSON.parse(vm.$el.querySelector('#nested_aaaa_bb_c').textContent)
- ).toMatchObject([1])
- expect(
- JSON.parse(vm.$el.querySelector('#nested_aaaa_bbb_cc').textContent)
- ).toMatchObject([1])
- expect(
- JSON.parse(vm.$el.querySelector('#nested_aaaa_bbb_cc').textContent)
- ).toMatchObject([1])
- expect(
- JSON.parse(vm.$el.querySelector('#nested_aaaa_bbbb').textContent)
- ).toMatchObject([{ value: 1 }])
+ const recursive = {
+ a: {
+ a: 'a',
+ b,
+ },
+ }
+
+ b.recursive = recursive
+ b.r = ref('r')
+
+ return {
+ recursive,
+ }
+ },
+ template: `
+
{{recursive.a.a}}
+
{{recursive.a.b.c}}
+
{{recursive.a.b.r}}
+
+
{{recursive.a.b.recursive.a.a}}
+
{{recursive.a.b.recursive.a.b.c}}
+
{{recursive.a.b.recursive.a.b.r}}
+
+
{{recursive.a.b.recursive.a.b.recursive.a.b.c}}
+
{{recursive.a.b.recursive.a.b.recursive.a.b.r}}
+
`,
+ }).$mount()
+ expect(vm.$el.querySelector('#recursive_a').textContent).toBe('a')
+ expect(vm.$el.querySelector('#recursive_b_c').textContent).toBe('c')
+ expect(vm.$el.querySelector('#recursive_b_r').textContent).toBe('r')
+
+ expect(vm.$el.querySelector('#recursive_b_recursive_a').textContent).toBe(
+ 'a'
+ )
+ expect(vm.$el.querySelector('#recursive_b_recursive_c').textContent).toBe(
+ 'c'
+ )
+ expect(vm.$el.querySelector('#recursive_b_recursive_r').textContent).toBe(
+ 'r'
+ )
+
+ expect(
+ vm.$el.querySelector('#recursive_b_recursive_recursive_c').textContent
+ ).toBe('c')
+
+ expect(
+ vm.$el.querySelector('#recursive_b_recursive_recursive_r').textContent
+ ).toBe('r')
+ })
})
it('should not unwrap built-in objects on the template', () => {