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.
This commit is contained in:
Evan You 2020-07-28 16:30:56 -04:00 committed by GitHub
parent de62cc040c
commit aa06b1034d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 39 additions and 10 deletions

View File

@ -1,15 +1,17 @@
export {
ref,
unref,
shallowRef,
isRef,
toRef,
toRefs,
unref,
proxyRefs,
customRef,
triggerRef,
Ref,
UnwrapRef,
ToRefs,
UnwrapRef,
ShallowUnwrapRef,
RefUnwrapBailTypes
} from './ref'
export {

View File

@ -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>

View File

@ -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)
}

View File

@ -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 &

View File

@ -8,6 +8,7 @@ export {
readonly,
// utilities
unref,
proxyRefs,
isRef,
toRef,
toRefs,
@ -125,6 +126,7 @@ export {
ComputedRef,
WritableComputedRef,
UnwrapRef,
ShallowUnwrapRef,
WritableComputedOptions,
ToRefs,
DeepReadonly

View File

@ -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