diff --git a/src/reactivity/unwrap.ts b/src/reactivity/unwrap.ts new file mode 100644 index 00000000..bf306ac1 --- /dev/null +++ b/src/reactivity/unwrap.ts @@ -0,0 +1,51 @@ +import { isRef } from './ref' +import { proxy, isFunction, isObject, isArray } from '../utils' +import { isReactive } from './reactive' + +export function unwrapRefProxy(value: any) { + if (isFunction(value)) { + return value + } + + if (isRef(value)) { + return value + } + + if (isArray(value)) { + return value + } + + if (isReactive(value)) { + return value + } + + if (!isObject(value)) { + return value + } + + if (!Object.isExtensible(value)) { + return value + } + + const obj: any = {} + + // copy symbols over + Object.getOwnPropertySymbols(value).forEach( + (s) => (obj[s] = (value as any)[s]) + ) + + for (const k of Object.keys(value)) { + const r = value[k] + // if is a ref, create a proxy to retrieve the value, + if (isRef(r)) { + const set = (v: any) => (r.value = v) + const get = () => r.value + + proxy(obj, k, { get, set }) + } else { + obj[k] = unwrapRefProxy(r) + } + } + + return obj +} diff --git a/src/setup.ts b/src/setup.ts index de5bbe0f..b1a40ae2 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -11,6 +11,7 @@ import { resolveSlots, createSlotProxy } from './helper' import { hasOwn, isPlainObject, assert, proxy, warn, isFunction } from './utils' import { ref } from './apis/state' import vmStateManager from './vmStateManager' +import { unwrapRefProxy } from './reactivity/unwrap' import { markReactive } from './reactivity/reactive' function asVmProperty( @@ -220,7 +221,7 @@ export function mixin(Vue: VueConstructor) { bindingValue = bindingValue.bind(vm) } // a non-reactive should not don't get reactivity - bindingValue = ref(markRaw(bindingValue)) + bindingValue = ref(markRaw(unwrapRefProxy(bindingValue))) } } asVmProperty(vm, name, bindingValue) diff --git a/test/setup.spec.js b/test/setup.spec.js index 1c0841ad..e273de06 100644 --- a/test/setup.spec.js +++ b/test/setup.spec.js @@ -5,6 +5,7 @@ const { createElement: h, provide, inject, + reactive, toRefs, } = require('../src') @@ -466,6 +467,107 @@ 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', + }, + }, + + aaa: reactive({ + b: ref('aaa'), + bb: { + c: ref('aaa'), + cc: 'aaa', + }, + }), + + aaaa: { + b: [1], + bb: ref([1]), + bbb: reactive({ + c: [1], + cc: ref([1]), + }), + bbbb: [ref(1)], + }, + } + + const refList = ref([ref('1'), ref('2'), ref('3')]) + const list = [ref('a'), ref('b')] + + 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 }}
+