From aa06b1034d8268fa15cb6b4b6916440701238b2d Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 28 Jul 2020 16:30:56 -0400 Subject: [PATCH] feat(reactivity): `proxyRefs` method and `ShallowUnwrapRefs` type (#1682) * feat(reactivity): `proxyRefs` method and `ShallowUnwrapRefs` type BREAKING CHANGE: template auto ref unwrapping are now applied shallowly, i.e. only at the root level. See https://github.com/vuejs/vue-next/pull/1682 for more details. --- packages/reactivity/src/index.ts | 6 +++-- packages/reactivity/src/ref.ts | 27 ++++++++++++++++++++- packages/runtime-core/src/component.ts | 6 ++--- packages/runtime-core/src/componentProxy.ts | 6 ++--- packages/runtime-core/src/index.ts | 2 ++ test-dts/defineComponent.test-d.tsx | 2 +- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index aaf53242592..b03e916d14a 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -1,15 +1,17 @@ export { ref, - unref, shallowRef, isRef, toRef, toRefs, + unref, + proxyRefs, customRef, triggerRef, Ref, - UnwrapRef, ToRefs, + UnwrapRef, + ShallowUnwrapRef, RefUnwrapBailTypes } from './ref' export { diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 25473b4cc8c..174494e87e9 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -1,7 +1,7 @@ import { track, trigger } from './effect' import { TrackOpTypes, TriggerOpTypes } from './operations' import { isObject, hasChanged } from '@vue/shared' -import { reactive, isProxy, toRaw } from './reactive' +import { reactive, isProxy, toRaw, isReactive } from './reactive' import { CollectionTypes } from './collectionHandlers' declare const RefSymbol: unique symbol @@ -71,6 +71,27 @@ export function unref(ref: T): T extends Ref ? V : T { return isRef(ref) ? (ref.value as any) : ref } +const shallowUnwrapHandlers: ProxyHandler = { + get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)), + set: (target, key, value, receiver) => { + const oldValue = target[key] + if (isRef(oldValue) && !isRef(value)) { + oldValue.value = value + return true + } else { + return Reflect.set(target, key, value, receiver) + } + } +} + +export function proxyRefs( + objectWithRefs: T +): ShallowUnwrapRef { + return isReactive(objectWithRefs) + ? objectWithRefs + : new Proxy(objectWithRefs, shallowUnwrapHandlers) +} + export type CustomRefFactory = ( track: () => void, trigger: () => void @@ -146,6 +167,10 @@ type BaseTypes = string | number | boolean */ export interface RefUnwrapBailTypes {} +export type ShallowUnwrapRef = { + [K in keyof T]: T[K] extends Ref ? V : T[K] +} + export type UnwrapRef = T extends Ref ? UnwrapRefSimple : UnwrapRefSimple diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index c0ea6ab3f3d..12eaa76bc93 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -1,10 +1,10 @@ import { VNode, VNodeChild, isVNode } from './vnode' import { - reactive, ReactiveEffect, pauseTracking, resetTracking, - shallowReadonly + shallowReadonly, + proxyRefs } from '@vue/reactivity' import { CreateComponentPublicInstance, @@ -548,7 +548,7 @@ export function handleSetupResult( } // setup returned bindings. // assuming a render function compiled from template is present. - instance.setupState = reactive(setupResult) + instance.setupState = proxyRefs(setupResult) if (__DEV__) { exposeSetupStateOnRenderContext(instance) } diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts index d2b78318ef7..e043d93eb4b 100644 --- a/packages/runtime-core/src/componentProxy.ts +++ b/packages/runtime-core/src/componentProxy.ts @@ -10,12 +10,12 @@ import { } from '@vue/shared' import { ReactiveEffect, - UnwrapRef, toRaw, shallowReadonly, ReactiveFlags, track, - TrackOpTypes + TrackOpTypes, + ShallowUnwrapRef } from '@vue/reactivity' import { ExtractComputedReturns, @@ -154,7 +154,7 @@ export type ComponentPublicInstance< $nextTick: typeof nextTick $watch: typeof instanceWatch } & P & - UnwrapRef & + ShallowUnwrapRef & D & ExtractComputedReturns & M & diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 757a21382bc..14283b5d46b 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -8,6 +8,7 @@ export { readonly, // utilities unref, + proxyRefs, isRef, toRef, toRefs, @@ -125,6 +126,7 @@ export { ComputedRef, WritableComputedRef, UnwrapRef, + ShallowUnwrapRef, WritableComputedOptions, ToRefs, DeepReadonly diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index dcbad6a0fad..f7dacfcbe21 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -146,7 +146,7 @@ describe('with object props', () => { // assert setup context unwrapping expectType(this.c) - expectType(this.d.e) + expectType(this.d.e.value) expectType(this.f.g) // setup context properties should be mutable