]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(reactivity): expose unref and shallowRef
authorEvan You <yyx990803@gmail.com>
Sat, 22 Feb 2020 03:39:32 +0000 (04:39 +0100)
committerEvan You <yyx990803@gmail.com>
Sat, 22 Feb 2020 03:39:32 +0000 (04:39 +0100)
packages/reactivity/__tests__/ref.spec.ts
packages/reactivity/src/index.ts
packages/reactivity/src/ref.ts
packages/runtime-core/src/componentProxy.ts
packages/runtime-core/src/index.ts
test-dts/ref.test-d.ts

index dc0cca6c8c3b7bb42bf48d839766c3129e9f6d56..6620f7fd9ce6cb2f227803aea047a211a7df902e 100644 (file)
@@ -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)
index 040dd8c673df873f61da600ea6a25d7bed4e5b73..21a9eae8f8ccec142ab6079a50b091be25c2832c 100644 (file)
@@ -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,
index 351dd1ddb5d5c55539dcd95df0561e58a370fd80..f88631130e755572317004fb806b178b46accbc5 100644 (file)
@@ -31,10 +31,22 @@ export function isRef(r: any): r is Ref {
 export function ref<T>(value: T): T extends Ref ? T : Ref<T>
 export function ref<T = any>(): Ref<T>
 export function ref(value?: unknown) {
+  return createRef(value)
+}
+
+export function shallowRef<T>(value: T): T extends Ref ? T : Ref<T>
+export function shallowRef<T = any>(): Ref<T>
+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<T>(ref: T): T extends Ref<infer V> ? V : T {
+  return isRef(ref) ? (ref.value as any) : ref
+}
+
 export function toRefs<T extends object>(
   object: T
 ): { [K in keyof T]: Ref<T[K]> } {
index 9e4511105236c5317f55582b4f19867657e5c4c8..a69d33da140fe7e9919f79cdf302e7e7b41e83cb 100644 (file)
@@ -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<any> = {
   get(target: ComponentInternalInstance, key: string) {
     // fast path for unscopables when using `with` block
@@ -115,7 +114,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
           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<any> = {
         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
index b26c9a8f98732f7f1e781117edfbac81ddb34976..8629d07f418d4500106e8120ba1c4a7d3d628af0 100644 (file)
@@ -3,6 +3,8 @@
 export const version = __VERSION__
 export {
   ref,
+  unref,
+  shallowRef,
   isRef,
   toRefs,
   reactive,
index ad8c22fef6132e932dd8cf05953cd1e6cc4acdc0..e1323fef1fb44312bb9dc5f0a8ae3a1144ae4766 100644 (file)
@@ -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<number>) {
   // ref coercing
@@ -11,6 +10,9 @@ function foo(arg: number | Ref<number>) {
   if (isRef(arg)) {
     expectType<Ref<number>>(arg)
   }
+
+  // ref unwrapping
+  expectType<number>(unref(arg))
 }
 
 foo(1)