From cca7e38f655579355b126ece0ef43ae9a156c81c Mon Sep 17 00:00:00 2001 From: wxzhang Date: Sun, 9 Jun 2024 17:26:51 +0800 Subject: [PATCH] add tests --- packages/core/src/form.tsx | 2 +- .../src/__tests__/computed.async.base.test.ts | 92 ++++++++ .../src/__tests__/computed.sync.base.test.ts | 38 ++++ .../src/__tests__/computed.sync.scope.test.ts | 214 ++++++++++++++++++ .../src/__tests__/computed.sync.test.ts | 72 ------ packages/reactive/src/computed/async.ts | 19 +- packages/reactive/src/computed/install.ts | 6 +- packages/reactive/src/computed/sync.ts | 4 +- packages/reactive/src/computed/types.ts | 10 +- packages/reactive/src/consts.ts | 3 + packages/reactive/src/context.ts | 27 +-- packages/reactive/src/store/store.ts | 15 +- packages/reactive/src/store/types.ts | 22 +- .../reactive/src/utils/getRelValuePath.ts | 29 ++- packages/reactive/tsup.config.ts | 10 +- packages/reactive/vite.config.ts | 1 + 16 files changed, 403 insertions(+), 161 deletions(-) create mode 100644 packages/reactive/src/__tests__/computed.async.base.test.ts create mode 100644 packages/reactive/src/__tests__/computed.sync.base.test.ts create mode 100644 packages/reactive/src/__tests__/computed.sync.scope.test.ts delete mode 100644 packages/reactive/src/__tests__/computed.sync.test.ts diff --git a/packages/core/src/form.tsx b/packages/core/src/form.tsx index dbe4a91..7dd5ec4 100644 --- a/packages/core/src/form.tsx +++ b/packages/core/src/form.tsx @@ -275,7 +275,7 @@ export function createForm(schema: FormSchema,op // 所有计算函数的上下文均指向根 computedThis: ()=>ComputedScopeRef.Root, // 计算函数作用域默认指向fields - computedScope: ()=>[FIELDS_STATE_KEY], + scope: ()=>[FIELDS_STATE_KEY], // 创建计算函数时的钩子函数,可以在创建前做一些不可描述的处理 onCreateComputed(valuePath,getter,options) { // 1. 只对validator进行处理,目的是使validate函数依赖于当前字段的值value,将使得validate函数的第一个参数总是当前字段的值 diff --git a/packages/reactive/src/__tests__/computed.async.base.test.ts b/packages/reactive/src/__tests__/computed.async.base.test.ts new file mode 100644 index 0000000..ecb634b --- /dev/null +++ b/packages/reactive/src/__tests__/computed.async.base.test.ts @@ -0,0 +1,92 @@ +import { test,expect, describe, beforeAll } from "vitest" +import { createStore,ComputedScopeRef,computed, IStore, Dict } from ".." +import { delay } from "flex-tools/async/delay" + +describe("简单异步计算",()=>{ + + test("异步计算原地替换创建AsyncComputed数据结构",async ()=>{ + return new Promise((resolve)=>{ + const store = createStore({ + price:2, + count:3, + total:async (scope:any)=>{ + await delay(10) + return scope.price * scope.count + } + }) + store.state.total + expect(store.state.total.loading).toBe(true) // 由于计算属性会马上执行一次,所以loading=true + expect(store.state.total.cancel).toBeDefined() + expect(store.state.total.error).toBeDefined() + expect(store.state.total.loading).toBeDefined() + expect(store.state.total.progress).toBeDefined() + expect(store.state.total.result).toBeUndefined() + expect(store.state.total.retry).toBeDefined() + expect(store.state.total.run).toBeDefined() + expect(store.state.total.timeout).toBeDefined() + resolve() + }) + }) + + test("异步计算初始化时不执行",async ()=>{ + return new Promise(async (resolve)=>{ + const store = createStore({ + price:2, + count:3, + total:computed(async (scope:any)=>{ + return scope.price * scope.count + },['price','count'],{ + immediate:false // 不马上执行,需要等等依赖变化多端时再执行 + }) + }) + expect(store.state.total.loading).toBe(false) + await delay(10) + resolve() + }) + }) + test("当异步计算属性依赖变化时自动重新计算",async ()=>{ + return new Promise((resolve)=>{ + const results:number[] = [] + const store = createStore({ + price:2, + count:3, + total:computed(async (scope:any)=>{ + return scope.price * scope.count + },['price','count'],{ + immediate:false // 不马上执行,需要等等依赖变化多端时再执行 + }) + }) + store.setState(draft=>draft.count++) + store.setState(draft=>draft.price++) + // 当计算函数执行完成后的回调 + store.on("computed",({path})=>{ + results.push(store.state.total.result) + + resolve() + }) + }) + }) + + + + test("默认this指向state",()=>{ + return new Promise((resolve)=>{ + const store = createStore({ + order:{ + price:2, + count:3, + total:computed(function(this:any){ + expect(this.order.price).toBe(2) + expect(this.order.count).toBe(3) + resolve() + }) + } + }) + store.state.order.total // 读取操作时创建计算属性 + }) + }) + + + +}) + diff --git a/packages/reactive/src/__tests__/computed.sync.base.test.ts b/packages/reactive/src/__tests__/computed.sync.base.test.ts new file mode 100644 index 0000000..26b1919 --- /dev/null +++ b/packages/reactive/src/__tests__/computed.sync.base.test.ts @@ -0,0 +1,38 @@ +import { test,expect, describe, beforeAll } from "vitest" +import { createStore,ComputedScopeRef,computed, IStore } from ".." + + + +describe("基本同步计算",()=>{ + + test("默认同步计算",async ()=>{ + const store = createStore({ + price:2, + count:3, + total:computed((scope)=>{ + return scope.price * scope.count + }) + }) + store.setState((draft)=>draft.count = 4) + expect(store.state.total).toBe(8) + }) + test("默认this指向state",()=>{ + return new Promise((resolve)=>{ + const store = createStore({ + order:{ + price:2, + count:3, + total:computed(function(this:any){ + expect(this.order.price).toBe(2) + expect(this.order.count).toBe(3) + resolve() + }) + } + }) + store.state.order.total // 读取操作时创建计算属性 + }) + }) + + + +}) diff --git a/packages/reactive/src/__tests__/computed.sync.scope.test.ts b/packages/reactive/src/__tests__/computed.sync.scope.test.ts new file mode 100644 index 0000000..31d6281 --- /dev/null +++ b/packages/reactive/src/__tests__/computed.sync.scope.test.ts @@ -0,0 +1,214 @@ +import { test,expect, describe, beforeAll } from "vitest" +import { createStore,ComputedScopeRef,computed } from ".." + +describe("计算函数的Scope指向",()=>{ + + test("默认Scope指向Current=order",()=>{ + return new Promise((resolve)=>{ + const store = createStore({ + order:{ + price:2, + count:3, + total:computed((scope)=>{ + expect(scope.price).toBe(2) + expect(scope.count).toBe(3) + resolve() + }) + } + }) + store.state.order.total // 读取操作时创建计算属性 + }) + + }) + test("Scope指向Root",()=>{ + return new Promise((resolve)=>{ + const store = createStore({ + order:{ + price:2, + count:3, + total:computed((scope)=>{ + expect(scope.order.price).toBe(2) + expect(scope.order.count).toBe(3) + resolve() + return scope.order.price * scope.order.count + }) + } + },{ + scope:()=>ComputedScopeRef.Root + }) + + store.state.order.total // 读取操作时创建计算属性 + }) + }) + test("Scope指向parent",()=>{ + return new Promise((resolve)=>{ + const store = createStore({ + root:{ + parent:{ + order:{ + price:2, + count:3, + total:computed((scope)=>{ + expect(scope.order.price).toBe(2) + expect(scope.order.count).toBe(3) + resolve() + return scope.order.price * scope.order.count + }) + } + } + } + },{ + scope:()=>ComputedScopeRef.Parent + }) + + store.state.root.parent.order.total // 读取操作时创建计算属性 + }) + }) + test("Scope指向Depends",()=>{ + return new Promise((resolve)=>{ + const store = createStore({ + root:{ + parent:{ + order:{ + price:2, + count:3, + // 当指定Depends时,同步计算通过执行计算函数不收集依赖的,所以第一次执行时,scope是空的 + // 所以ComputedScopeRef.Depends在同步计算下是无效的 + total:computed((scope)=>{ + expect(scope.length).toBe(0) + resolve() + return 0 + }) + } + } + } + },{ + scope:()=>ComputedScopeRef.Depends + }) + store.state.root.parent.order.total // 读取操作时创建计算属性 + }) + }) + test("Scope指向字符串指定的绝对路径",()=>{ + // + return new Promise((resolve)=>{ + const store = createStore({ + root:{ + parent:{ + order:{ + price:2, + count:3, + total:computed((scope)=>{ + expect(scope).toBe(1) + resolve() + return 0 + }) + } + }, + a:1 + } + },{ + scope:()=>"/root/a" // 绝对路径需要以/开头 + }) + store.state.root.parent.order.total // 读取操作时创建计算属性 + }) + }) + + test("Scope指向字符串指定的当前对象的相对路径",()=>{ + //注意: . 代表的是不是total,而是total所在的对象 + return new Promise((resolve)=>{ + const store = createStore({ + root:{ + parent:{ + order:{ + price:2, + count:3, + total:computed((scope)=>{ + expect(scope).toBe(2) + resolve() + return 0 + }) + } + }, + a:1 + } + },{ + scope:()=>"./price" //.代表的是order + }) + store.state.root.parent.order.total // 读取操作时创建计算属性 + }) + }) + test("Scope指向字符串指定的当前对象父对象的相对路径",()=>{ + //注意: .. 代表的是不是total,而是total所在的对象 + return new Promise((resolve)=>{ + const store = createStore({ + root:{ + parent:{ + order:{ + price:2, + count:3, + total:computed((scope)=>{ + expect(scope).toBe(100) + resolve() + return 0 + }) + }, + x:100 + }, + a:1 + } + },{ + scope:()=>"../x" // ..指向parent + }) + store.state.root.parent.order.total // 读取操作时创建计算属性 + }) + }) + test("Scope指向字符串指定的多级相对父对象路径",()=>{ + //注意: .. 代表的是不是total,而是total所在的对象 + return new Promise((resolve)=>{ + const store = createStore({ + root:{ + parent:{ + order:{ + price:2, + count:3, + total:computed((scope)=>{ + expect(scope).toBe(1) + resolve() + return 0 + }) + }, + x:100 + }, + a:1 + } + },{ + scope:()=>"../../a" // + }) + store.state.root.parent.order.total // 读取操作时创建计算属性 + }) + }) + test("使用字符串数组作为Scope指向的绝对路径",()=>{ + //注意: .. 代表的是不是total,而是total所在的对象 + return new Promise((resolve)=>{ + const store = createStore({ + root:{ + parent:{ + order:{ + price:2, + count:3, + total:computed((scope)=>{ + expect(scope).toBe(3) + resolve() + return 0 + }) + } + } + } + },{ + scope:()=>["root","parent","order","count"] + }) + store.state.root.parent.order.total // 读取操作时创建计算属性 + }) + }) + +}) \ No newline at end of file diff --git a/packages/reactive/src/__tests__/computed.sync.test.ts b/packages/reactive/src/__tests__/computed.sync.test.ts deleted file mode 100644 index dc9143d..0000000 --- a/packages/reactive/src/__tests__/computed.sync.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { test,expect, describe, beforeAll } from "vitest" -import { createStore,ComputedScopeRef,computed, IStore } from ".." - - -const Account = { - order:{ - price:2, - count:3, - total:computed((scope)=>{ - return scope.price * scope.count - },{ - id:"total", - }) - } -} - - -describe("基本同步计算",()=>{ - - test("默认同步计算",async ()=>{ - const store = createStore({ - price:2, - count:3, - total:computed((scope)=>{ - return scope.price * scope.count - }) - }) - store.setState((draft)=>draft.count = 4) - expect(store.state.total).toBe(8) - }) -}) - -describe("Scope指向",()=>{ - - test("默认Scope指向Current=order",()=>{ - return new Promise((resolve)=>{ - const store = createStore({ - order:{ - price:2, - count:3, - total:computed((scope)=>{ - expect(scope.price).toBe(2) - expect(scope.count).toBe(3) - resolve() - }) - } - }) - store.state.order.total // 读取操作时创建计算属性 - }) - - }) - test("Scope指向Root",()=>{ - return new Promise((resolve)=>{ - const store = createStore({ - order:{ - price:2, - count:3, - total:computed((scope)=>{ - expect(scope.order.price).toBe(2) - expect(scope.order.count).toBe(3) - resolve() - return scope.order.price * scope.order.count - }) - } - },{ - computedScope:()=>ComputedScopeRef.Root - }) - - store.state.order.total // 读取操作时创建计算属性 - }) - }) -}) \ No newline at end of file diff --git a/packages/reactive/src/computed/async.ts b/packages/reactive/src/computed/async.ts index 5b5647f..d5e2c80 100644 --- a/packages/reactive/src/computed/async.ts +++ b/packages/reactive/src/computed/async.ts @@ -14,7 +14,7 @@ import { switchValue } from "flex-tools/misc/switchValue"; import { Dict } from "../types"; import { delay } from 'flex-tools/async/delay'; import { OBJECT_PATH_DELIMITER } from '../consts'; -import { getComputedContextDraft, getComputedScopeDraft } from '../context'; +import { getComputedScopeDraft } from '../context'; import { AsyncComputedGetter, AsyncComputedObject, ComputedOptions, ComputedParams, ComputedProgressbar } from './types'; import type { ComputedDescriptor, ComputedRunContext } from './types'; import { IReactiveReadHookParams } from '../reactives/types'; @@ -26,13 +26,13 @@ import { executeStoreHooks } from './utils'; * 创建异步计算属性的数据结构 * */ -export function createAsyncComputedObject(store:IStore,mutateId:string,valueObj:Partial){ +export function createAsyncComputedObject(store:IStore,mutateId:string,valueObj:Partial){ return Object.assign({ - // value : undefined, loading : false, timeout : 0, retry : 0, // 重试次数,3表示最多重试3次 error : null, + result : undefined, progress: 0, run : markRaw(skipComputed((args:Dict) => { return store.reactiveable.runComputed(mutateId,Object.assign({},args)); @@ -43,8 +43,9 @@ export function createAsyncComputedObject(store:IStore },valueObj) } -export function setAsyncComputedObject(stateCtx:any,draft:any,resultPath:string[],mutateDesc:string,valueObj:Partial){ - const asyncObj = createAsyncComputedObject(stateCtx,mutateDesc,valueObj) + +export function setAsyncComputedObject(store:IStore,draft:any,resultPath:string[],mutateId:string,valueObj:Partial){ + const asyncObj = createAsyncComputedObject(store,mutateId,valueObj) const reusltValue = getVal(draft,resultPath) Object.assign(reusltValue,asyncObj,valueObj) } @@ -95,7 +96,7 @@ async function executeComputedGetter(draft:any,computedRu const { timeout=0,retry=[0,0],selfState } = computedOptions const setState = selfState ? selfState.setState : store.setState // - const thisDraft = selfState ? draft : getComputedContextDraft(store,draft,computedRunContext, computedOptions) + const thisDraft = selfState ? selfState: draft const scopeDraft = selfState ? draft : getComputedScopeDraft(store,draft,computedRunContext, computedOptions) @@ -211,7 +212,7 @@ function createComputed(computedRunContext:ComputedRunCon if(toComputedResult=='self'){ // 原地替换 setVal(draft, valuePath, createAsyncComputedObject(store, mutateId,{result: initial})) }else{ // 更新到其他地方 - setAsyncComputedObject(store.stateCtx,draft,resultPath, mutateId,{result: initial}) + setAsyncComputedObject(store,draft,resultPath, mutateId,{result: initial}) // 删除原始的计算属性 const p = getVal(draft,valuePath.slice(0,valuePath.length-1)) delete p[valuePath[valuePath.length-1]] @@ -233,7 +234,9 @@ function createComputed(computedRunContext:ComputedRunCon computedRunContext.isMutateRunning=true computedRunContext.dependValues = values // 即所依赖项的值 try{ - return await executeComputedGetter(draft,computedRunContext,finalComputedOptions,store) + const r= await executeComputedGetter(draft,computedRunContext,finalComputedOptions,store) + store.emit("computed",{path:valuePath,id:mutateId}) + return r }finally{ computedRunContext.isMutateRunning=false } diff --git a/packages/reactive/src/computed/install.ts b/packages/reactive/src/computed/install.ts index 0cdde5b..20b463f 100644 --- a/packages/reactive/src/computed/install.ts +++ b/packages/reactive/src/computed/install.ts @@ -25,23 +25,21 @@ export function installComputed(params:IReactiveReadHookP computedObject = createComputedMutate(params,store); }else if (isAsyncFunction(descriptor)) { // 简单的异步计算函数,没有通过computed函数创建,此时由于没有指定依赖,所以只会执行一次 params.value = () => ({ - fn: descriptor, + getter: descriptor, options: { depends : [], // 未指定依赖 initial : undefined, // 也没有初始化值 immediate: true, // 立即执行 enable : true, - context :store.options.computedThis && store.options.computedThis('Computed'), }, }); computedObject = createAsyncComputedMutate(params,store); }else { // 简单的同步计算函数,没有通过computed函数创建 params.value = () => ({ - fn: descriptor, + getter: descriptor, options: { initial : undefined, enable : true, - context : store.options.computedThis && store.options.computedThis('Computed'), } }) // 直接声明同步计算函数,使用全局配置的计算上下文 diff --git a/packages/reactive/src/computed/sync.ts b/packages/reactive/src/computed/sync.ts index 8640a5d..71825fc 100644 --- a/packages/reactive/src/computed/sync.ts +++ b/packages/reactive/src/computed/sync.ts @@ -4,7 +4,7 @@ import { StoreDefine } from "../store/types"; import { getComputedId, getVal, setVal } from "../utils"; import { ComputedDescriptorParams, ComputedGetter, ComputedOptions, ComputedRunContext, RuntimeComputedOptions } from './types'; -import { getComputedContextDraft, getComputedScopeDraft } from '../context'; +import { getComputedScopeDraft } from '../context'; import { IStore } from '../store/types'; import { IReactiveReadHookParams } from "../reactives/types"; import { ComputedObject } from "./computedObject"; @@ -29,7 +29,7 @@ function createComputed(computedRunContext:ComputedRunCon computedRunContext.dependValues = values // 1. 根据配置参数获取计算函数的上下文对象 - const thisDraft = selfState ? draft : getComputedContextDraft(store,draft,computedRunContext, computedOptions) + const thisDraft = selfState ? selfState : draft const scopeDraft = selfState ? draft : getComputedScopeDraft(store,draft,computedRunContext, computedOptions) // 2. 执行getter函数 diff --git a/packages/reactive/src/computed/types.ts b/packages/reactive/src/computed/types.ts index e97f951..7d3e8aa 100644 --- a/packages/reactive/src/computed/types.ts +++ b/packages/reactive/src/computed/types.ts @@ -2,10 +2,10 @@ * 类型 */ -import { IMutateWitness, IOperateParams, ISharedCtx } from "helux"; -import type { ComputedScope, ComputedContext, StoreDefine, ITargetState } from "../store/types"; +import { IOperateParams, ISharedCtx } from "helux"; +import type { ComputedScope, ITargetState } from "../store/types"; import { Dict } from "../types" -import { WatchDescriptor, WatchDescriptorCreator } from "../watch"; +import { WatchDescriptorCreator } from "../watch"; // 指向helux的IOperateParams类型,但是我们只用到其是的部分类型 @@ -86,7 +86,6 @@ export interface ComputedProgressbar{ export interface ComputedOptions { // 计算函数的唯一标识,如果未指定,则自动生成一个唯一标识 id? : string - context? : ComputedContext // 计算函数的this scope? : ComputedScope // 计算函数的第一个参数 initial? : Value // 异步计算,默认情况下,通过typeof(fn)=="async function"来判断是否是异步计算函数 @@ -198,8 +197,7 @@ export interface ComputedProgressbar{ export type AsyncComputedGetter = (scopeDraft:Scope,options:Required) => Promise // 当调用run方法时,用来传参覆盖原始的计算参数 - export type RuntimeComputedOptions = Pick + export type RuntimeComputedOptions = Pick export type AsyncComputedObject ={ loading? : boolean; diff --git a/packages/reactive/src/consts.ts b/packages/reactive/src/consts.ts index 6a38876..f003aa1 100644 --- a/packages/reactive/src/consts.ts +++ b/packages/reactive/src/consts.ts @@ -2,3 +2,6 @@ export const OBJECT_PATH_DELIMITER = '/' export const SKIP_COMPUTED= Symbol('SKIP_COMPUTED') + + + \ No newline at end of file diff --git a/packages/reactive/src/context.ts b/packages/reactive/src/context.ts index 590526d..5b4c533 100644 --- a/packages/reactive/src/context.ts +++ b/packages/reactive/src/context.ts @@ -23,7 +23,7 @@ import { IOperateParams } from "helux"; import { OBJECT_PATH_DELIMITER } from "./consts"; import { type ComputedScope, ComputedScopeRef, StoreOptions, StoreDefine, IStore } from "./store/types"; -import { getValueByPath } from "./utils"; +import { getRelValuePath, getValueByPath } from "./utils"; import { ComputedOptions, ComputedRunContext, StateComputedType } from "./computed/types"; /* @@ -47,7 +47,7 @@ function getContextOptions(state: any,computedCtxOption?: ComputedScope,storeCtx } export type GetComputedContextOptions ={ - type:'context' | 'scope', // 要获取的是什么: context或scope + // type:'context' | 'scope', // 要获取的是什么: context或scope computedType:StateComputedType, // 取值, 'Computed' | 'Watch dependValues:any[], // 当前计算函数依赖值,或watch的侦听的值 valuePath:string[], @@ -67,13 +67,13 @@ export type GetComputedContextOptions ={ */ export function getComputedContext(draft: any,params:GetComputedContextOptions) { - const { dependValues, type, valuePath, funcOptions, storeOptions,computedType } = params; + const { dependValues, valuePath, funcOptions, storeOptions,computedType } = params; let rootDraft = draft; // 1. 执行hook:可以在hook函数中修改计算函数的根上下文以及相关配置参数 if (typeof storeOptions.onComputedContext == "function") { - const newDraft = storeOptions.onComputedContext.call(draft,draft,{computedType,contextType:type,valuePath}); + const newDraft = storeOptions.onComputedContext.call(draft,draft,{computedType,valuePath}); if (newDraft !== undefined) { rootDraft = newDraft; } @@ -81,10 +81,7 @@ export function getComputedContext(draft: a const parentPath = valuePath.length>=1 ? valuePath.slice(0, valuePath.length - 1) : []; // 2. 读取计算函数的上下文配置参数 - const contexRef = getContextOptions(draft, - type=='context' ? funcOptions.context : funcOptions.scope, - type=='context' ? (storeOptions.computedThis && storeOptions.computedThis(computedType)) : (storeOptions.computedScope && storeOptions.computedScope(computedType)) - ) + const contexRef = getContextOptions(draft,funcOptions.scope, (storeOptions.scope && storeOptions.scope(computedType))) // 3. 根据配置参数获取计算函数的上下文对象 try { @@ -97,7 +94,7 @@ export function getComputedContext(draft: a }else if (contexRef === ComputedScopeRef.Depends) { // 异步计算的依赖值 return Array.isArray(dependValues) ? dependValues.map(dep=>typeof(dep)=='function' ? dep() : dep) : []; }else if (typeof contexRef == "string") { // 当前对象的指定键 - return getValueByPath(draft, [...parentPath, ...contexRef.split(OBJECT_PATH_DELIMITER)]); + return getValueByPath(draft, getRelValuePath(valuePath,contexRef)) }else if (Array.isArray(contexRef)) { // 从根对象开始的完整路径 if(contexRef.length>0 && contexRef[0].startsWith("@")){ const finalKeys = getValueByPath(draft, [...contexRef[0].substring(1).split(OBJECT_PATH_DELIMITER),...contexRef.slice(1)]); @@ -125,22 +122,14 @@ export function getComputedContext(draft: a * @param params * @returns */ -export function getComputedRefDraft(store:IStore,draft: any,computedRunContext:ComputedRunContext,computedOptions: ComputedOptions,type:'context' | 'scope') { +export function getComputedScopeDraft(store:IStore,draft: any,computedRunContext:ComputedRunContext,computedOptions: ComputedOptions) { const { valuePath,dependValues:values } = computedRunContext return getComputedContext(draft,{ dependValues: values, - type, valuePath, funcOptions:computedOptions, storeOptions:store.options, computedType:'Computed' }) } - - -export function getComputedScopeDraft(store:IStore,draft: any,computedRunContext:ComputedRunContext,computedOptions: ComputedOptions) { - return getComputedRefDraft(store,draft,computedRunContext,computedOptions,'scope') -} -export function getComputedContextDraft(store:IStore,draft: any,computedRunContext:ComputedRunContext,computedOptions: ComputedOptions) { - return getComputedRefDraft(store,draft,computedRunContext,computedOptions,'context') -} \ No newline at end of file + \ No newline at end of file diff --git a/packages/reactive/src/store/store.ts b/packages/reactive/src/store/store.ts index a9a21ae..9f2ea91 100644 --- a/packages/reactive/src/store/store.ts +++ b/packages/reactive/src/store/store.ts @@ -1,5 +1,3 @@ -import { sharex } from "helux" -import type { ComputedState } from "../computed/types" import { ComputedScopeRef, IStore, StoreDefine, StoreEvents, StoreOptions } from '../types'; import { ComputedObjects } from '../computed'; import { installExtends } from "../extends" @@ -20,8 +18,7 @@ export function createStore(data:T,options? const opts = Object.assign({ id : getRndId(), debug : true, - computedThis : ()=>ComputedScopeRef.Root, - computedScope: ()=>ComputedScopeRef.Current, + scope: ()=>ComputedScopeRef.Current, singleton : true, },options) as StoreOptions @@ -55,19 +52,11 @@ export function createStore(data:T,options? } }) as Reactiveable - - // store.stateCtx = sharex>(data as any, { - // stopArrDep: false, - // moduleName: opts.id, - // onRead: (params) => { - // installExtends(params as any,store as IStore); - // } - // }); store.state = store.reactiveable.state store.emit("created") store.useState = createUseState(store) store.setState = createSetState(store) - store.enableComputed = (value:boolean=true)=>store.stateCtx.setEnableMutate(value) + // store.enableComputed = (value:boolean=true)=>store.reactiveable.setEnableMutate(value) // store.sync = store.stateCtx.sync // 侦听 store.watch = createWatch(store) diff --git a/packages/reactive/src/store/types.ts b/packages/reactive/src/store/types.ts index 5348a3f..b66503b 100644 --- a/packages/reactive/src/store/types.ts +++ b/packages/reactive/src/store/types.ts @@ -15,21 +15,22 @@ import { ComputedObject } from "../computed/computedObject"; export type StoreDefine = State + export enum ComputedScopeRef{ Root = 'root', Current = 'current', Parent = 'parent', Depends = 'depends', // 指向依赖数组 Self = 'self' // 指向自身,默认值 -} +} + -export type NonDependsScopeRef = Exclude; + // 指定Store中计算函数的上下文,如果是字符串代表是当前对象的指定键,如果是string[],则代表是当前Store对象的完整路径 // 当ComputedContext是一个字符串并且以@开头时,有个特殊含义,即是一个路径指向: // 如:{fields:{ user:"address",address:"user" }},如果scope=@user,代表的当前scope对象指向的user属性的值所指向的对象,在这里实质传入的是address export type ComputedScope = ComputedScopeRef | string | string[] | ((state:any)=>string | string[] | ComputedScopeRef) -export type ComputedContext = NonDependsScopeRef | string | string[] | ((state:any)=>string | string[] | NonDependsScopeRef) export type StateGetter = (state:State)=>Value export type StateSetter = (state:State,value:Value)=>void @@ -42,8 +43,8 @@ export interface StoreOptions{ debug:boolean // 计算函数的默认上下文,即传入的给计算函数的draft对象是根state还是所在的对象或父对象 // 如果未指定时,同步计算的上下文指向current,异步指定的上下文指向root - computedThis:(computedType:StateComputedType)=>ComputedContext - computedScope:(computedType:StateComputedType)=> ComputedScope + // computedThis:(computedType:StateComputedType)=>ComputedContext + scope:(computedType:StateComputedType)=> ComputedScope // 是否是单例模式,如果是单例模式,那么所有的计算属性都是共享的,否则每个实例都有自己的计算属性 // =false时会对传入的data进行深度克隆,这样就可以创建多个互相不干扰的实例 singleton:boolean @@ -61,10 +62,10 @@ export interface StoreOptions{ onCreateComputed:(this:IStore,keyPath:string[],getter:Function,options:ComputedOptions)=> void | (()=>any) /** - * 在传递给计算函数的context和scope时调用 - * 可以返回一个新的context和scope来代替默认的 + * 在传递给计算函数的scope时调用 + * 可以返回一个新的scope来代替默认的 */ - onComputedContext(draft:any,options:{computedType:StateComputedType, contextType:'context' | 'scope',valuePath:string[]}):any + onComputedContext(draft:any,options:{computedType:StateComputedType, valuePath:string[]}):any // 输出日志信息 log:(message:any,level?:'log' | 'error' | 'warn')=>void /** @@ -79,8 +80,8 @@ export interface StoreOptions{ export type StoreEvents = { - created: undefined; // 响应对象创建后 - + created: undefined; // 响应对象创建后 + computed:{path:string[],id:string} // 当计算函数执行成功后 }; @@ -97,7 +98,6 @@ export type IStore = { enableComputed : (value:boolean)=>void options : StoreOptions reactiveable : Reactiveable - stateCtx : ISharedCtx> reactive : IReactive // 计算 createComputed : ReturnType diff --git a/packages/reactive/src/utils/getRelValuePath.ts b/packages/reactive/src/utils/getRelValuePath.ts index 978a263..6b062ac 100644 --- a/packages/reactive/src/utils/getRelValuePath.ts +++ b/packages/reactive/src/utils/getRelValuePath.ts @@ -21,37 +21,34 @@ x = [ 'a', 'b', 'c', 'd', 'e', 'x' ] ../../../../../../x = [ 'x' ] * * - * @param path + * @param curPath * @param relPath * @returns */ -export function getRelValuePath(path:string[],relPath:'self' | 'root' | 'parent' | 'current' | string[] | string ):string[]{ - if(!Array.isArray(path)) throw new Error('path must be an array') +export function getRelValuePath(curPath:string[],relPath:'self' | 'root' | 'parent' | 'current' | string[] | string ):string[]{ + if(!Array.isArray(curPath)) throw new Error('curPath must be an array') if(relPath === 'self'){ - return path + return curPath }else if(relPath === 'root'){ return [] }else if(relPath === 'parent'){ - return path.slice(0,-2) + return curPath.slice(0,-2) }else if(relPath === 'current'){ - return path.slice(0,-1) + return curPath.slice(0,-1) }else if(typeof(relPath) === 'string'){ // 字符串支持相对路径语法,如"../" 或 "./" 或 "xxx" if(relPath.startsWith('./')){ - return [...path.slice(0,-1),...relPath.slice(2).split(OBJECT_PATH_DELIMITER)] + return [...curPath.slice(0,-1),...relPath.slice(2).split(OBJECT_PATH_DELIMITER)] }else if(relPath.startsWith('../')){ // 父路径 - return getRelValuePath(path.slice(0,-1),relPath.slice(3)) + return getRelValuePath(curPath.slice(0,-1),relPath.slice(3)) + }else if(relPath.startsWith(OBJECT_PATH_DELIMITER)){ // 绝对路径 + relPath = relPath.replace(/^(\/)*/,"") + return [...relPath.split(OBJECT_PATH_DELIMITER)] }else{ - // 如果路径中包含".",则自动转换为"/"并给出警告 - // 使用/路径分割符的原因是,可以使用./或../等相对路径语法 - if(relPath.includes(".")){ - console.warn('[@speedform/reactive] The dependency path uses "/" as the separator, and will automatically convert') - relPath=relPath.replaceAll(".","/") - } - return [...path.slice(0,-1),...relPath.split(OBJECT_PATH_DELIMITER)] + return [...curPath.slice(0,-1),...relPath.split(OBJECT_PATH_DELIMITER)] } }else if(Array.isArray(relPath)){ return relPath } - return path + return curPath } \ No newline at end of file diff --git a/packages/reactive/tsup.config.ts b/packages/reactive/tsup.config.ts index 539094a..b308d19 100644 --- a/packages/reactive/tsup.config.ts +++ b/packages/reactive/tsup.config.ts @@ -32,12 +32,4 @@ export default defineConfig([ noExternal:['helux','flex-tools'] } ]) - - - // esbuildPlugins:[ - // // @ts-ignore - // copy({ - // source:['package.json','README.md','LICENSE'], - // target:"dist/" - // }) - // ] \ No newline at end of file + \ No newline at end of file diff --git a/packages/reactive/vite.config.ts b/packages/reactive/vite.config.ts index 0a6207c..6eab6c4 100644 --- a/packages/reactive/vite.config.ts +++ b/packages/reactive/vite.config.ts @@ -8,5 +8,6 @@ export default defineConfig({ globals: true, environment: 'jsdom', setupFiles: './vitest-setup.ts', + include:['**\/*.{test,spec}.ts'] }, });