Przeglądaj źródła

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.
Evan You 5 lat temu
rodzic
commit
aa06b1034d

+ 4 - 2
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 {

+ 26 - 1
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<T>(ref: T): T extends Ref<infer V> ? V : T {
   return isRef(ref) ? (ref.value as any) : ref
 }
 
+const shallowUnwrapHandlers: ProxyHandler<any> = {
+  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<T extends object>(
+  objectWithRefs: T
+): ShallowUnwrapRef<T> {
+  return isReactive(objectWithRefs)
+    ? objectWithRefs
+    : new Proxy(objectWithRefs, shallowUnwrapHandlers)
+}
+
 export type CustomRefFactory<T> = (
   track: () => void,
   trigger: () => void
@@ -146,6 +167,10 @@ type BaseTypes = string | number | boolean
  */
 export interface RefUnwrapBailTypes {}
 
+export type ShallowUnwrapRef<T> = {
+  [K in keyof T]: T[K] extends Ref<infer V> ? V : T[K]
+}
+
 export type UnwrapRef<T> = T extends Ref<infer V>
   ? UnwrapRefSimple<V>
   : UnwrapRefSimple<T>

+ 3 - 3
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)
     }

+ 3 - 3
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<B> &
+  ShallowUnwrapRef<B> &
   D &
   ExtractComputedReturns<C> &
   M &

+ 2 - 0
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

+ 1 - 1
test-dts/defineComponent.test-d.tsx

@@ -146,7 +146,7 @@ describe('with object props', () => {
 
       // assert setup context unwrapping
       expectType<number>(this.c)
-      expectType<string>(this.d.e)
+      expectType<string>(this.d.e.value)
       expectType<GT>(this.f.g)
 
       // setup context properties should be mutable