diff --git a/packages/reactivity/__tests__/ref.spec.ts b/packages/reactivity/__tests__/ref.spec.ts index dc0cca6c8c3..6620f7fd9ce 100644 --- a/packages/reactivity/__tests__/ref.spec.ts +++ b/packages/reactivity/__tests__/ref.spec.ts @@ -1,5 +1,14 @@ -import { ref, effect, reactive, isRef, toRefs, Ref } from '../src/index' +import { + ref, + effect, + reactive, + isRef, + toRefs, + Ref, + isReactive +} from '../src/index' import { computed } from '@vue/runtime-dom' +import { shallowRef, unref } from '../src/ref' describe('reactivity/ref', () => { it('should hold a value', () => { @@ -129,6 +138,26 @@ describe('reactivity/ref', () => { expect(tupleRef.value[4].value).toBe(1) }) + test('unref', () => { + expect(unref(1)).toBe(1) + expect(unref(ref(1))).toBe(1) + }) + + test('shallowRef', () => { + const sref = shallowRef({ a: 1 }) + expect(isReactive(sref.value)).toBe(false) + + let dummy + effect(() => { + dummy = sref.value.a + }) + expect(dummy).toBe(1) + + sref.value = { a: 2 } + expect(isReactive(sref.value)).toBe(false) + expect(dummy).toBe(2) + }) + test('isRef', () => { expect(isRef(ref(1))).toBe(true) expect(isRef(computed(() => 1))).toBe(true) diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index 040dd8c673d..21a9eae8f8c 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -1,4 +1,4 @@ -export { ref, isRef, toRefs, Ref, UnwrapRef } from './ref' +export { ref, unref, shallowRef, isRef, toRefs, Ref, UnwrapRef } from './ref' export { reactive, isReactive, diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 351dd1ddb5d..f88631130e7 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -31,10 +31,22 @@ export function isRef(r: any): r is Ref { export function ref(value: T): T extends Ref ? T : Ref export function ref(): Ref export function ref(value?: unknown) { + return createRef(value) +} + +export function shallowRef(value: T): T extends Ref ? T : Ref +export function shallowRef(): Ref +export function shallowRef(value?: unknown) { + return createRef(value, true) +} + +function createRef(value: unknown, shallow = false) { if (isRef(value)) { return value } - value = convert(value) + if (!shallow) { + value = convert(value) + } const r = { _isRef: true, get value() { @@ -42,7 +54,7 @@ export function ref(value?: unknown) { return value }, set value(newVal) { - value = convert(newVal) + value = shallow ? newVal : convert(newVal) trigger( r, TriggerOpTypes.SET, @@ -54,6 +66,10 @@ export function ref(value?: unknown) { return r } +export function unref(ref: T): T extends Ref ? V : T { + return isRef(ref) ? (ref.value as any) : ref +} + export function toRefs( object: T ): { [K in keyof T]: Ref } { diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts index 9e451110523..a69d33da140 100644 --- a/packages/runtime-core/src/componentProxy.ts +++ b/packages/runtime-core/src/componentProxy.ts @@ -13,7 +13,8 @@ import { isRef, isReactive, Ref, - ComputedRef + ComputedRef, + unref } from '@vue/reactivity' import { warn } from './warning' import { Slots } from './componentSlots' @@ -84,8 +85,6 @@ const enum AccessTypes { OTHER } -const unwrapRef = (val: unknown) => (isRef(val) ? val.value : val) - export const PublicInstanceProxyHandlers: ProxyHandler = { get(target: ComponentInternalInstance, key: string) { // fast path for unscopables when using `with` block @@ -115,7 +114,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler = { case AccessTypes.DATA: return data[key] case AccessTypes.CONTEXT: - return unwrapRef(renderContext[key]) + return unref(renderContext[key]) case AccessTypes.PROPS: return propsProxy![key] // default: just fallthrough @@ -125,7 +124,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler = { return data[key] } else if (hasOwn(renderContext, key)) { accessCache![key] = AccessTypes.CONTEXT - return unwrapRef(renderContext[key]) + return unref(renderContext[key]) } else if (type.props != null) { // only cache other properties when instance has declared (this stable) // props diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index b26c9a8f987..8629d07f418 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -3,6 +3,8 @@ export const version = __VERSION__ export { ref, + unref, + shallowRef, isRef, toRefs, reactive, diff --git a/test-dts/ref.test-d.ts b/test-dts/ref.test-d.ts index ad8c22fef61..e1323fef1fb 100644 --- a/test-dts/ref.test-d.ts +++ b/test-dts/ref.test-d.ts @@ -1,6 +1,5 @@ import { expectType } from 'tsd' -import { Ref, ref } from './index' -import { isRef } from '@vue/reactivity' +import { Ref, ref, isRef, unref } from './index' function foo(arg: number | Ref) { // ref coercing @@ -11,6 +10,9 @@ function foo(arg: number | Ref) { if (isRef(arg)) { expectType>(arg) } + + // ref unwrapping + expectType(unref(arg)) } foo(1)