From 5cc10c4e08f7027724c582736b4b8199a0f388e0 Mon Sep 17 00:00:00 2001 From: Tang <985907374@qq.com> Date: Mon, 27 Nov 2023 22:25:51 +0800 Subject: [PATCH] =?UTF-8?q?pref:=20computed=20=E6=94=AF=E6=8C=81=E4=BC=A0?= =?UTF-8?q?=E5=85=A5set?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reactivity/__tests__/computed.spec.ts | 19 ++++++++++++- packages/reactivity/src/computed.ts | 27 +++++++++++++++---- packages/shared/src/index.ts | 3 +++ 3 files changed, 43 insertions(+), 6 deletions(-) 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