feat(reactivity): expose unref and shallowRef

This commit is contained in:
Evan You 2020-02-22 04:39:32 +01:00
parent 0c67201942
commit e9024bf1b7
6 changed files with 59 additions and 11 deletions

View 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)

View 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,

View 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
}
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]> } {

View 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

View File

@ -3,6 +3,8 @@
export const version = __VERSION__
export {
ref,
unref,
shallowRef,
isRef,
toRefs,
reactive,

View 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)