Match-id-a412f18457208f656771020342fcc35095290f8c

This commit is contained in:
* 2022-06-28 21:50:36 +08:00 committed by *
parent 70027b0a1f
commit 81459f999d
5 changed files with 161 additions and 161 deletions

View File

@ -8,9 +8,7 @@ import {IObserver} from '../types';
*/ */
export class HooklessObserver implements IObserver { export class HooklessObserver implements IObserver {
listeners = []; listeners:(() => void)[] = [];
vNodeKeys: null;
keyVNodes: null;
useProp(key: string): void { useProp(key: string): void {
} }

View File

@ -9,11 +9,12 @@ import { VNode } from '../../renderer/vnode/VNode';
import { IObserver } from '../types'; import { IObserver } from '../types';
export class Observer implements IObserver { export class Observer implements IObserver {
vNodeKeys = new WeakMap(); vNodeKeys = new WeakMap();
keyVNodes = new Map(); keyVNodes = new Map();
listeners = []; listeners:(()=>void)[] = [];
useProp(key: string): void { useProp(key: string): void {
const processingVNode = getProcessingVNode(); const processingVNode = getProcessingVNode();
@ -84,7 +85,7 @@ export class Observer implements IObserver {
} }
} }
clearByVNode(vNode: VNode): void { clearByVNode(vNode: Vnode): void {
const keys = this.vNodeKeys.get(vNode); const keys = this.vNodeKeys.get(vNode);
if (keys) { if (keys) {
keys.forEach((key: any) => { keys.forEach((key: any) => {

View File

@ -1,9 +1,10 @@
import { createObjectProxy } from './handlers/ObjectProxyHandler'; import {createObjectProxy} from './handlers/ObjectProxyHandler';
import { Observer } from './Observer'; import {Observer} from './Observer';
import { HooklessObserver } from './HooklessObserver'; import {HooklessObserver} from './HooklessObserver';
import { isArray, isCollection, isObject } from '../CommonUtils'; import {isArray, isCollection, isObject} from '../CommonUtils';
import { createArrayProxy } from './handlers/ArrayProxyHandler'; import {createArrayProxy} from './handlers/ArrayProxyHandler';
import { createCollectionProxy } from './handlers/CollectionProxyHandler'; import {createCollectionProxy} from './handlers/CollectionProxyHandler';
import { IObserver } from '../types';
const OBSERVER_KEY = Symbol('_horizonObserver'); const OBSERVER_KEY = Symbol('_horizonObserver');
@ -28,7 +29,7 @@ export function createProxy(rawObj: any, hookObserver = true): any {
} }
// 创建Observer // 创建Observer
let observer = getObserver(rawObj); let observer:IObserver = getObserver(rawObj);
if (!observer) { if (!observer) {
observer = hookObserver ? new Observer() : new HooklessObserver(); observer = hookObserver ? new Observer() : new HooklessObserver();
rawObj[OBSERVER_KEY] = observer; rawObj[OBSERVER_KEY] = observer;

View File

@ -3,77 +3,71 @@ import { useEffect, useRef } from '../../renderer/hooks/HookExternal';
import { getProcessingVNode } from '../../renderer/GlobalVar'; import { getProcessingVNode } from '../../renderer/GlobalVar';
import { createProxy } from '../proxy/ProxyHandler'; import { createProxy } from '../proxy/ProxyHandler';
import readonlyProxy from '../proxy/readonlyProxy'; import readonlyProxy from '../proxy/readonlyProxy';
import { StoreHandler, StoreConfig, UserActions, UserComputedValues } from '../types'; import { StoreHandler, StoreConfig, UserActions, UserComputedValues, StoreActions, ComputedValues, ActionFunction, Action, QueuedStoreActions } from '../types';
import { Observer } from '../proxy/Observer'; import { Observer } from '../proxy/Observer';
import { FunctionComponent, ClassComponent } from '../Constants'; import { FunctionComponent, ClassComponent } from '../Constants';
const storeMap = new Map(); const storeMap = new Map<string,StoreHandler<any,any,any>>();
function isPromise(obj: any): boolean { function isPromise(obj: any): boolean {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
} }
export function createStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>( type PlannedAction<S extends object,F extends ActionFunction<S>>={
config: StoreConfig<S, A, C> action:string,
): () => StoreHandler<S, A, C> { payload: any[],
let handler: any = { resolve: ReturnType<F>
$subscribe: null, }
$unsubscribe: null,
$state: null,
$config: config,
$queue: null,
$actions: {},
$computed: {},
};
const obj = { export function createStore<S extends object,A extends UserActions<S>,C extends UserComputedValues<S>>(config: StoreConfig<S,A,C>): () => StoreHandler<S,A,C> {
...config, //create a local shalow copy to ensure consistency (if user would change the config object after store creation)
config, config = {
plannedActions: [], id:config.id,
rawState: config.state, options: config.options,
rawActions: { ...config.actions }, state: config.state,
}; actions: config.actions ? {...config.actions}:undefined,
computed: config.computed ? {...config.computed}:undefined
}
// 校验 // 校验
if (Object.prototype.toString.call(obj) !== '[object Object]') { if (Object.prototype.toString.call(config) !== '[object Object]') {
throw new Error('store obj must be pure object'); throw new Error('store obj must be pure object');
} }
const proxyObj = createProxy(obj.state, !obj.options?.suppressHooks); const proxyObj = createProxy(config.state, !config.options?.suppressHooks);
proxyObj.$pending = false; proxyObj.$pending = false;
handler.$subscribe = listener => {
const $subscribe = (listener) => {
proxyObj.addListener(listener); proxyObj.addListener(listener);
}; };
handler.$unsubscribe = listener => {
const $unsubscribe = (listener) => {
proxyObj.removeListener(listener); proxyObj.removeListener(listener);
}; };
obj.rawState = obj.state;
obj.state = proxyObj;
handler.$state = obj.state; const plannedActions:PlannedAction<S,ActionFunction<S>>[] = [];
const $actions:Partial<StoreActions<S,A>>={}
handler.$config = obj.config; const $queue:Partial<StoreActions<S,A>> = {};
const $computed:Partial<ComputedValues<S,C>>={}
// handles.$reset = ()=>{ const handler = {
// const keys = Object.keys(obj.state); $subscribe,
// Object.entries(obj.defaultState).forEach(([key,value])=>{ $unsubscribe,
// obj.state[key]=value; $actions:$actions as StoreActions<S,A>,
// }); $state:proxyObj,
// keys.forEach(key => { $computed: $computed as ComputedValues<S,C>,
// if(!obj.defaultState[key]){ $config:config,
// delete obj.state[key]; $queue: $queue as QueuedStoreActions<S,A>,
// } } as StoreHandler<S,A,C>;
// });
// };
function tryNextAction() { function tryNextAction() {
if (!obj.plannedActions.length) { if (!plannedActions.length) {
proxyObj.$pending = false; proxyObj.$pending = false;
return; return;
} }
const nextAction = obj.plannedActions.shift(); const nextAction = plannedActions.shift()!;
const result = obj.rawActions[nextAction.action].bind(self, obj.state)(...nextAction.payload); const result = config.actions ? config.actions[nextAction.action].bind(self, proxyObj)(...nextAction.payload) : undefined;
if (isPromise(result)) { if (isPromise(result)) {
result.then(value => { result.then(value => {
@ -87,62 +81,64 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
} }
// 包装actions // 包装actions
Object.keys(obj.actions).forEach(key => { if(config.actions){
(obj.actions as any)[key] = handler[key] = function Wrapped(...payload) { Object.keys(config.actions).forEach(action => {
return obj.rawActions[key].bind(self, obj.state)(...payload); ($queue as any)[action] = (...payload) => {
}; return new Promise((resolve) => {
}); if (!proxyObj.$pending) {
proxyObj.$pending = true;
const result = config.actions![action].bind(self, proxyObj)(...payload);
handler.$queue = {}; if (isPromise(result)) {
Object.keys(obj.rawActions).forEach(action => { result.then((value) => {
handler.$queue[action] = (...payload) => { resolve(value);
return new Promise(resolve => { tryNextAction();
if (!proxyObj.$pending) { });
proxyObj.$pending = true; } else {
const result = obj.rawActions[action].bind(self, obj.state)(...payload); resolve(result);
if (isPromise(result)) {
result.then(value => {
resolve(value);
tryNextAction(); tryNextAction();
}); }
} else { } else {
resolve(result); plannedActions.push({
tryNextAction(); action,
payload,
resolve
});
} }
} else { });
obj.plannedActions.push({ };
action,
payload, ($actions as any)[action] = function Wrapped(...payload) {
resolve, return config.actions![action].bind(self, proxyObj)(...payload);
}); };
}
// direct store access
Object.defineProperty(handler, action, {
writable: false,
value: $actions[action]
});
});
}
if (config.computed) {
Object.keys(config.computed).forEach((key) => {
($computed as any)[key] = config.computed![key].bind(handler, readonlyProxy(proxyObj));
// direct store access
Object.defineProperty(handler, key, {
get: $computed[key] as ()=>any
});
});
}
// direct state access
if(config.state){
Object.keys(config.state).forEach(key => {
Object.defineProperty(handler, key, {
get: () => proxyObj[key]
}); });
};
});
handler.$actions = obj.actions;
// native getters
Object.keys(obj.state).forEach(key => {
Object.defineProperty(handler, key, {
get: () => obj.state[key],
});
});
// computed
if (obj.computed) {
Object.keys(obj.computed).forEach(key => {
// supports access through attributes
Object.defineProperty(handler, key, {
get: obj.computed[key].bind(handler, readonlyProxy(obj.state)),
});
// supports access through function
(obj.computed as any)[key] = obj.computed[key].bind(handler, readonlyProxy(obj.state));
}); });
} }
handler.$computed = obj.computed || {};
if (config.id) { if (config.id) {
storeMap.set(config.id, handler); storeMap.set(config.id, handler);
@ -213,13 +209,11 @@ export function useStore<S extends object, A extends UserActions<S>, C extends U
): StoreHandler<S, A, C> { ): StoreHandler<S, A, C> {
const storeObj = storeMap.get(id); const storeObj = storeMap.get(id);
if (!storeObj.$config.options?.suppressHooks) { if (storeObj && !storeObj.$config.options?.suppressHooks) hookStore();
hookStore();
}
return storeObj; return storeObj as StoreHandler<S,A,C>;
} }
export function clearStore(id: string): void { export function clearStore(id:string):void {
storeMap.delete(id); storeMap.delete(id);
} }

View File

@ -1,9 +1,4 @@
export interface IObserver { export interface IObserver {
vNodeKeys: WeakMap<any, any>;
keyVNodes: Map<any, any>;
listeners: (() => void)[];
useProp: (key: string) => void; useProp: (key: string) => void;
@ -22,54 +17,65 @@ export interface IObserver {
clearByVNode: (vNode: any) => void; clearByVNode: (vNode: any) => void;
} }
type UserActions<S extends object> = { [K: string]: StateFunction<S> }; type RemoveFirstFromTuple<T extends any[]> =
type UserComputedValues<S extends object> = { [K: string]: StateFunction<S> }; T['length'] extends 0 ? [] :
(((...b: T) => void) extends (a, ...b: infer I) => void ? I : [])
type StateFunction<S extends object> = (state: S, ...args: any[]) => any;
type StoreActions<S extends object, A extends UserActions<S>> = { [K in keyof A]: A[K] }; type UserActions<S extends object> = { [K:string]: ActionFunction<S> };
type ComputedValues<S extends object, C extends UserComputedValues<S>> = { [K in keyof C]: C[K] }; type UserComputedValues<S extends object> = { [K:string]: ComputedFunction<S> };
type ActionFunction<S extends object> = (state: S, ...args: any[]) => any;
type ComputedFunction<S extends object> = (state: S) => any;
type Action<T extends UserActions<?>> = (...args:RemoveFirstFromTuple<Parameters<T>>)=>ReturnType<T>
type AsyncAction<T extends UserActions<?>> = (...args:RemoveFirstFromTuple<Parameters<T>>)=>Promise<ReturnType<T>>
type StoreActions<S extends object,A extends UserActions<S>> = { [K in keyof A]: Action<A[K]> };
type QueuedStoreActions<S extends object,A extends UserActions<S>> = { [K in keyof A]: AsyncAction<A[K]> };
type ComputedValues<S extends object,C extends UserComputedValues<S>> = { [K in keyof C]: ReturnType<C[K]> };
type PostponedAction = (state: object, ...args: any[]) => Promise<any>; type PostponedAction = (state: object, ...args: any[]) => Promise<any>;
type PostponedActions = { [key: string]: PostponedAction }; type PostponedActions = { [key:string]: PostponedAction }
export type StoreHandler<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = { export type StoreHandler<S extends object,A extends UserActions<S>,C extends UserComputedValues<S>> =
$subscribe: (listener: () => void) => void; {$subscribe: ((listener: () => void) => void),
$unsubscribe: (listener: () => void) => void; $unsubscribe: ((listener: () => void) => void),
$state: S; $state: S,
$config: StoreConfig<S, A, C>; $config: StoreConfig<S,A,C>,
$queue: StoreActions<S, A>; $queue: QueuedStoreActions<S,A>,
$actions: StoreActions<S, A>; $actions: StoreActions<S,A>,
$computed: StoreActions<S, A>; $computed: ComputedValues<S,C>,
reduxHandler?: ReduxStoreHandler; reduxHandler?:ReduxStoreHandler}
} & { [K in keyof S]: S[K] } & &
{ [K in keyof A]: A[K] } & {[K in keyof S]: S[K]}
{ [K in keyof C]: C[K] }; &
{[K in keyof A]: Action<A[K]>}
&
{[K in keyof C]: ReturnType<C[K]>}
export type StoreConfig<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = { export type StoreConfig<S extends object,A extends UserActions<S>,C extends UserComputedValues<S>> = {
state?: S; state?: S,
options?: { suppressHooks?: boolean }; options?:{suppressHooks?: boolean},
actions?: A; actions?: A,
id?: string; id?: string,
computed?: C; computed?: C
}; }
type ReduxStoreHandler = { type ReduxStoreHandler = {
reducer: (state: any, action: { type: string }) => any; reducer:(state:any,action:{type:string})=>any,
dispatch: (action: { type: string }) => void; dispatch:(action:{type:string})=>void,
getState: () => any; getState:()=>any,
subscribe: (listener: () => void) => (listener: () => void) => void; subscribe:(listener:()=>void)=>((listener:()=>void)=>void)
}; replaceReducer: (reducer: (state:any,action:{type:string})=>any)=>void
_horizonXstore: StoreHandler
}
type ReduxAction = { type ReduxAction = {
type: string; type:string
}; }
type ReduxMiddleware = ( type ReduxMiddleware = (store:ReduxStoreHandler, extraArgument?:any) =>
store: ReduxStoreHandler, (next:((action:ReduxAction)=>any)) =>
extraArgument?: any (action:(
) => ( ReduxAction|
next: (action: ReduxAction) => any ((dispatch:(action:ReduxAction)=>void,store:ReduxStoreHandler,extraArgument?:any)=>any)
) => ( )) => ReduxStoreHandler
action:
| ReduxAction
| ((dispatch: (action: ReduxAction) => void, store: ReduxStoreHandler, extraArgument?: any) => any)
) => ReduxStoreHandler;