refactor: simplify implementations

since we output es5, classes are more verbose then necessary
This commit is contained in:
Evan You 2022-05-26 02:34:15 +08:00
parent 282ebb0893
commit a16271280a
3 changed files with 54 additions and 98 deletions

View File

@ -13,7 +13,7 @@ export interface ComputedRef<T = any> extends WritableComputedRef<T> {
}
export interface WritableComputedRef<T> extends Ref<T> {
readonly effect: { stop(): void }
readonly effect: Watcher
}
export type ComputedGetter<T> = (...args: any[]) => T
@ -53,54 +53,31 @@ export function computed<T>(
setter = getterOrOptions.set
}
return new ComputedRefImpl(
getter,
setter,
onlyGetter,
isServerRendering()
) as any
}
const watcher = isServerRendering()
? null
: new Watcher(currentInstance, getter, noop, { lazy: true })
class ComputedRefImpl<T> {
public dep?: Dep = undefined
public readonly __v_isRef = true
public readonly effect
private _watcher: Watcher | null
constructor(
private _getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
public readonly __v_isReadonly: boolean,
isSSR: boolean
) {
const watcher = (this._watcher = isSSR
? null
: new Watcher(currentInstance, _getter, noop, { lazy: true }))
this.effect = {
stop() {
watcher && watcher.teardown()
return {
__v_isRef: true,
__v_isReadonly: onlyGetter,
// some libs rely on the presence effect for checking computed refs
// from normal refs, but the implementation doesn't matter
effect: watcher,
get value() {
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
} else {
return getter()
}
},
set value(newVal) {
setter(newVal)
}
}
get value() {
const watcher = this._watcher
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
} else {
return this._getter()
}
}
set value(newValue: T) {
this._setter(newValue)
}
} as any
}

View File

@ -76,33 +76,21 @@ export type CustomRefFactory<T> = (
set: (value: T) => void
}
class CustomRefImpl<T> {
public dep = new Dep()
private readonly _get: ReturnType<CustomRefFactory<T>>['get']
private readonly _set: ReturnType<CustomRefFactory<T>>['set']
public readonly __v_isRef = true
constructor(factory: CustomRefFactory<T>) {
const { get, set } = factory(
() => this.dep.depend(),
() => this.dep.notify()
)
this._get = get
this._set = set
}
get value() {
return this._get()
}
set value(newVal) {
this._set(newVal)
}
}
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
return new CustomRefImpl(factory) as any
const dep = new Dep()
const { get, set } = factory(
() => dep.depend(),
() => dep.notify()
)
return {
__v_isRef: true,
get value() {
return get()
},
set value(newVal) {
set(newVal)
}
} as any
}
export type ToRefs<T = any> = {
@ -120,25 +108,6 @@ export function toRefs<T extends object>(object: T): ToRefs<T> {
return ret
}
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
constructor(
private readonly _object: T,
private readonly _key: K,
private readonly _defaultValue?: T[K]
) {}
get value() {
const val = this._object[this._key]
return val === undefined ? (this._defaultValue as T[K]) : val
}
set value(newVal) {
this._object[this._key] = newVal
}
}
export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
export function toRef<T extends object, K extends keyof T>(
@ -158,9 +127,19 @@ export function toRef<T extends object, K extends keyof T>(
defaultValue?: T[K]
): ToRef<T[K]> {
const val = object[key]
return isRef(val)
? val
: (new ObjectRefImpl(object, key, defaultValue) as any)
if (isRef(val)) {
return val as any
}
return {
__v_isRef: true,
get value() {
const val = object[key]
return val === undefined ? (defaultValue as T[K]) : val
},
set value(newVal) {
object[key] = newVal
}
} as any
}
/**

View File

@ -131,7 +131,7 @@ describe('reactivity/computed', () => {
expect(dummy).toBe(undefined)
value.foo = 1
expect(dummy).toBe(1)
cValue.effect.stop()
cValue.effect.teardown()
value.foo = 2
expect(dummy).toBe(1)
})
@ -221,7 +221,7 @@ describe('reactivity/computed', () => {
it('should expose value when stopped', () => {
const x = computed(() => 1)
x.effect.stop()
x.effect.teardown()
expect(x.value).toBe(1)
})