d901b6bea8
WeakSets and WeakMaps shows degrading performance as the amount of observed objects increases. Using hidden keys result in better performance especially when repeatedly creating large amounts of reactive proxies. This also makes it possible to more efficiently declare non-reactive objects in userland.
77 lines
1.9 KiB
TypeScript
77 lines
1.9 KiB
TypeScript
import { effect, ReactiveEffect, trigger, track } from './effect'
|
|
import { TriggerOpTypes, TrackOpTypes } from './operations'
|
|
import { Ref } from './ref'
|
|
import { isFunction, NOOP } from '@vue/shared'
|
|
|
|
export interface ComputedRef<T = any> extends WritableComputedRef<T> {
|
|
readonly value: T
|
|
}
|
|
|
|
export interface WritableComputedRef<T> extends Ref<T> {
|
|
readonly effect: ReactiveEffect<T>
|
|
}
|
|
|
|
export type ComputedGetter<T> = (ctx?: any) => T
|
|
export type ComputedSetter<T> = (v: T) => void
|
|
|
|
export interface WritableComputedOptions<T> {
|
|
get: ComputedGetter<T>
|
|
set: ComputedSetter<T>
|
|
}
|
|
|
|
export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
|
|
export function computed<T>(
|
|
options: WritableComputedOptions<T>
|
|
): WritableComputedRef<T>
|
|
export function computed<T>(
|
|
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
|
|
) {
|
|
let getter: ComputedGetter<T>
|
|
let setter: ComputedSetter<T>
|
|
|
|
if (isFunction(getterOrOptions)) {
|
|
getter = getterOrOptions
|
|
setter = __DEV__
|
|
? () => {
|
|
console.warn('Write operation failed: computed value is readonly')
|
|
}
|
|
: NOOP
|
|
} else {
|
|
getter = getterOrOptions.get
|
|
setter = getterOrOptions.set
|
|
}
|
|
|
|
let dirty = true
|
|
let value: T
|
|
let computed: ComputedRef<T>
|
|
|
|
const runner = effect(getter, {
|
|
lazy: true,
|
|
// mark effect as computed so that it gets priority during trigger
|
|
computed: true,
|
|
scheduler: () => {
|
|
if (!dirty) {
|
|
dirty = true
|
|
trigger(computed, TriggerOpTypes.SET, 'value')
|
|
}
|
|
}
|
|
})
|
|
computed = {
|
|
__v_isRef: true,
|
|
// expose effect so computed can be stopped
|
|
effect: runner,
|
|
get value() {
|
|
if (dirty) {
|
|
value = runner()
|
|
dirty = false
|
|
}
|
|
track(computed, TrackOpTypes.GET, 'value')
|
|
return value
|
|
},
|
|
set value(newValue: T) {
|
|
setter(newValue)
|
|
}
|
|
} as any
|
|
return computed
|
|
}
|