mirror of https://github.com/vuejs/core.git
feat: ref peek
This commit is contained in:
parent
56be3dd4db
commit
cc326a72d2
|
@ -1150,4 +1150,54 @@ describe('reactivity/computed', () => {
|
||||||
const t2 = performance.now()
|
const t2 = performance.now()
|
||||||
expect(t2 - t1).toBeLessThan(process.env.CI ? 100 : 30)
|
expect(t2 - t1).toBeLessThan(process.env.CI ? 100 : 30)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('peek', () => {
|
||||||
|
it('should return updated value', () => {
|
||||||
|
const value = reactive<{ foo?: number }>({})
|
||||||
|
const cValue = computed(() => value.foo)
|
||||||
|
expect(cValue.peek()).toBe(undefined)
|
||||||
|
value.foo = 1
|
||||||
|
expect(cValue.peek()).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should compute lazily', () => {
|
||||||
|
const value = reactive<{ foo?: number }>({})
|
||||||
|
const getter = vi.fn(() => value.foo)
|
||||||
|
const cValue = computed(getter)
|
||||||
|
|
||||||
|
// lazy
|
||||||
|
expect(getter).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
expect(cValue.peek()).toBe(undefined)
|
||||||
|
expect(getter).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
// should not compute again
|
||||||
|
cValue.peek()
|
||||||
|
expect(getter).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
// should not compute until needed
|
||||||
|
value.foo = 1
|
||||||
|
expect(getter).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
// now it should compute
|
||||||
|
expect(cValue.peek()).toBe(1)
|
||||||
|
expect(getter).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
// should not compute again
|
||||||
|
cValue.peek()
|
||||||
|
expect(getter).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not trigger effect', () => {
|
||||||
|
const value = reactive<{ foo?: number }>({})
|
||||||
|
const cValue = computed(() => value.foo)
|
||||||
|
let dummy
|
||||||
|
effect(() => {
|
||||||
|
dummy = cValue.peek()
|
||||||
|
})
|
||||||
|
expect(dummy).toBe(undefined)
|
||||||
|
value.foo = 1
|
||||||
|
expect(dummy).toBe(undefined)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -534,4 +534,26 @@ describe('reactivity/ref', () => {
|
||||||
// @ts-expect-error internal field
|
// @ts-expect-error internal field
|
||||||
expect(objectRefValue._value).toBe(1)
|
expect(objectRefValue._value).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('peek', () => {
|
||||||
|
it('should hold a value', () => {
|
||||||
|
const a = ref(1)
|
||||||
|
expect(a.peek()).toBe(1)
|
||||||
|
a.value = 2
|
||||||
|
expect(a.peek()).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not be reactive', () => {
|
||||||
|
const a = ref(1)
|
||||||
|
let dummy
|
||||||
|
const fn = vi.fn(() => {
|
||||||
|
dummy = a.peek()
|
||||||
|
})
|
||||||
|
effect(fn)
|
||||||
|
expect(fn).toHaveBeenCalledTimes(1)
|
||||||
|
expect(dummy).toBe(1)
|
||||||
|
a.value = 2
|
||||||
|
expect(fn).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,9 +26,11 @@ interface BaseComputedRef<T, S = T> extends Ref<T, S> {
|
||||||
|
|
||||||
export interface ComputedRef<T = any> extends BaseComputedRef<T> {
|
export interface ComputedRef<T = any> extends BaseComputedRef<T> {
|
||||||
readonly value: T
|
readonly value: T
|
||||||
|
peek: () => T
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WritableComputedRef<T, S = T> extends BaseComputedRef<T, S> {
|
export interface WritableComputedRef<T, S = T> extends BaseComputedRef<T, S> {
|
||||||
|
peek: () => T
|
||||||
[WritableComputedRefSymbol]: true
|
[WritableComputedRefSymbol]: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +153,12 @@ export class ComputedRefImpl<T = any> implements Subscriber {
|
||||||
warn('Write operation failed: computed value is readonly')
|
warn('Write operation failed: computed value is readonly')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peek(): T {
|
||||||
|
refreshComputed(this)
|
||||||
|
|
||||||
|
return this._value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,6 +34,10 @@ export interface Ref<T = any, S = T> {
|
||||||
[RefSymbol]: true
|
[RefSymbol]: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RefWithPeek<T = any, S = T> extends Ref<T, S> {
|
||||||
|
peek: () => T
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a value is a ref object.
|
* Checks if a value is a ref object.
|
||||||
*
|
*
|
||||||
|
@ -54,7 +58,9 @@ export function isRef(r: any): r is Ref {
|
||||||
*/
|
*/
|
||||||
export function ref<T>(
|
export function ref<T>(
|
||||||
value: T,
|
value: T,
|
||||||
): [T] extends [Ref] ? IfAny<T, Ref<T>, T> : Ref<UnwrapRef<T>, UnwrapRef<T> | T>
|
): [T] extends [Ref]
|
||||||
|
? IfAny<T, Ref<T>, T>
|
||||||
|
: RefWithPeek<UnwrapRef<T>, UnwrapRef<T> | T>
|
||||||
export function ref<T = any>(): Ref<T | undefined>
|
export function ref<T = any>(): Ref<T | undefined>
|
||||||
export function ref(value?: unknown) {
|
export function ref(value?: unknown) {
|
||||||
return createRef(value, false)
|
return createRef(value, false)
|
||||||
|
@ -66,6 +72,10 @@ export type ShallowRef<T = any, S = T> = Ref<T, S> & {
|
||||||
[ShallowRefMarker]?: true
|
[ShallowRefMarker]?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ShallowRefWithPeek<T = any, S = T> = ShallowRef<T, S> & {
|
||||||
|
peek: () => T
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shallow version of {@link ref}.
|
* Shallow version of {@link ref}.
|
||||||
*
|
*
|
||||||
|
@ -156,6 +166,10 @@ class RefImpl<T = any> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peek() {
|
||||||
|
return this._rawValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue