diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 0bc9f811..f0495d9c 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -1,6 +1,7 @@ import { computed } from "../src/computed"; import { reactive } from "../src/reactive"; -import {vi} from 'vitest' +import { ref } from '../src/ref' +import { vi } from 'vitest' describe("computed", () => { it("happy path", () => { @@ -47,4 +48,20 @@ describe("computed", () => { cValue.value; expect(getter).toHaveBeenCalledTimes(2); }); + it('should support setter', () => { + const n = ref(1) + const plusOne = computed({ + get: () => n.value + 1, + set: val => { + n.value = val - 1 + } + }) + + expect(plusOne.value).toBe(2) + n.value++ + expect(plusOne.value).toBe(3) + + plusOne.value = 0 + expect(n.value).toBe(-1) + }) }); diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index b941af4b..17e4f158 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,16 +1,17 @@ import { createDep } from "./dep"; import { ReactiveEffect } from "./effect"; import { trackRefValue, triggerRefValue } from "./ref"; - +import { isFunction } from '@mini-vue/shared' export class ComputedRefImpl { public dep: any; public effect: ReactiveEffect; private _dirty: boolean; private _value - - constructor(getter) { + public readonly readonly + constructor(getter, private readonly setter, isReadonly) { this._dirty = true; + this.readonly = isReadonly this.dep = createDep(); this.effect = new ReactiveEffect(getter, () => { // scheduler @@ -38,8 +39,24 @@ export class ComputedRefImpl { return this._value; } + + set value(newVal) { + this.setter(newVal) + } } -export function computed(getter) { - return new ComputedRefImpl(getter); +export function computed(getterOrOptions) { + let getter + let setter + const isOnlyGetter = isFunction(getterOrOptions) + if (isOnlyGetter) { + getter = getterOrOptions + setter = () => { + console.warn('computed value is readonly') + } + } else { + getter = getterOrOptions.get + setter = getterOrOptions.set + } + return new ComputedRefImpl(getter, setter, isOnlyGetter || !setter); } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 5f89898a..0f030a75 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -53,3 +53,6 @@ const hyphenateRE = /\B([A-Z])/g; */ export const hyphenate = (str: string) => str.replace(hyphenateRE, "-$1").toLowerCase(); + +export const isFunction = (val: unknown): val is Function => + typeof val === 'function' \ No newline at end of file