Merge pull request 'Optimize Storage Modular' (#16) from dev-mrkbear into master
Reviewed-on: http://git.mrkbear.com/MrKBear/mini-dlpu-v3/pulls/16
This commit is contained in:
commit
de67ab0d68
@ -2,9 +2,9 @@
|
||||
|
||||
掌上教务处作为工业大学的社区开源项目,自从2017年开始已有近5年岁月,在无数同学的贡献之下,为大家打造便捷的校园服务。
|
||||
|
||||
__*!!!注意!!!*__
|
||||
__*!!!警告!!!*__
|
||||
|
||||
请在主仓库提交代码,而非镜像仓库!
|
||||
请在主仓库提交代码,而非镜像仓库!在镜像仓库提交的代码将会在同步时被覆盖!
|
||||
|
||||
主仓库: http://git.mrkbear.com/MrKBear/mini-dlpu-v3
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { IAppAPIParam } from "./core/Api";
|
||||
import { IAppStorageParam, Storage, IStorageData } from "./core/Storage";
|
||||
import { Logger, LevelLogLabel, LifeCycleLogLabel } from "./core/Logger";
|
||||
|
||||
|
||||
App<IAppAPIParam>({
|
||||
App<IAppAPIParam & IAppStorageParam>({
|
||||
|
||||
/**
|
||||
* API 模块需要的全局数据
|
||||
@ -16,7 +17,7 @@ App<IAppAPIParam>({
|
||||
/**
|
||||
* 存储缓存键值
|
||||
*/
|
||||
// storageCache: new Set<string>(),
|
||||
storage: new Map<string, Storage<IStorageData>>(),
|
||||
|
||||
/**
|
||||
* 小程序加载时
|
||||
|
@ -2,27 +2,18 @@ export type EventType = string | symbol;
|
||||
|
||||
// An event handler can take an optional event argument
|
||||
// and should not return a value
|
||||
export type Handler<T = unknown> = (event: T) => void;
|
||||
export type WildcardHandler<T = Record<string, unknown>> = (
|
||||
type: keyof T,
|
||||
event: T[keyof T]
|
||||
) => void;
|
||||
export type Handler<T = any> = (event: T) => void;
|
||||
|
||||
// An array of all currently registered event handlers for a type
|
||||
export type EventHandlerList<T = unknown> = Array<Handler<T>>;
|
||||
export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;
|
||||
export type EventHandlerList<T = any> = Array<Handler<T>>;
|
||||
|
||||
// A map of event types and their corresponding event handlers.
|
||||
export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<
|
||||
keyof Events | '*',
|
||||
EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>
|
||||
export type EventHandlerMap<Events extends Record<EventType, any>> = Map<
|
||||
keyof Events,
|
||||
EventHandlerList<Events[keyof Events]>
|
||||
>;
|
||||
|
||||
type GenericEventHandler<Events extends Record<EventType, unknown>> =
|
||||
| Handler<Events[keyof Events]>
|
||||
| WildcardHandler<Events>;
|
||||
|
||||
export class Emitter<Events extends Record<EventType, unknown>> {
|
||||
export class Emitter<Events extends Record<EventType, any>> {
|
||||
|
||||
/**
|
||||
* A Map of event names to registered handler functions.
|
||||
@ -41,17 +32,14 @@ export class Emitter<Events extends Record<EventType, unknown>> {
|
||||
this.all!.set(type, [] as EventHandlerList<Events[keyof Events]>);
|
||||
}
|
||||
|
||||
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
|
||||
on(type: '*', handler: WildcardHandler<Events>): void;
|
||||
|
||||
/**
|
||||
* Register an event handler for the given type.
|
||||
* @param {string|symbol} type Type of event to listen for, or `'*'` for all events
|
||||
* @param {string|symbol} type Type of event to listen for
|
||||
* @param {Function} handler Function to call in response to given event
|
||||
* @memberOf mitt
|
||||
*/
|
||||
public on<Key extends keyof Events>(type: Key, handler: GenericEventHandler<Events>) {
|
||||
const handlers: Array<GenericEventHandler<Events>> | undefined = this.all!.get(type);
|
||||
public on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>) {
|
||||
const handlers: Array<Handler<Events[Key]>> | undefined = this.all!.get(type);
|
||||
if (handlers) {
|
||||
handlers.push(handler);
|
||||
}
|
||||
@ -60,18 +48,15 @@ export class Emitter<Events extends Record<EventType, unknown>> {
|
||||
}
|
||||
}
|
||||
|
||||
off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
|
||||
off(type: '*', handler: WildcardHandler<Events>): void;
|
||||
|
||||
/**
|
||||
* Remove an event handler for the given type.
|
||||
* If `handler` is omitted, all handlers of the given type are removed.
|
||||
* @param {string|symbol} type Type of event to unregister `handler` from, or `'*'`
|
||||
* @param {string|symbol} type Type of event to unregister `handler` from
|
||||
* @param {Function} [handler] Handler function to remove
|
||||
* @memberOf mitt
|
||||
*/
|
||||
public off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler<Events>) {
|
||||
const handlers: Array<GenericEventHandler<Events>> | undefined = this.all!.get(type);
|
||||
public off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>) {
|
||||
const handlers: Array<Handler<Events[Key]>> | undefined = this.all!.get(type);
|
||||
if (handlers) {
|
||||
if (handler) {
|
||||
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
|
||||
@ -82,20 +67,14 @@ export class Emitter<Events extends Record<EventType, unknown>> {
|
||||
}
|
||||
}
|
||||
|
||||
emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
|
||||
emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
|
||||
|
||||
/**
|
||||
* Invoke all handlers for the given type.
|
||||
* If present, `'*'` handlers are invoked after type-matched handlers.
|
||||
*
|
||||
* Note: Manually firing '*' handlers is not supported.
|
||||
*
|
||||
* @param {string|symbol} type The event type to invoke
|
||||
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
|
||||
* @memberOf mitt
|
||||
*/
|
||||
emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
|
||||
emit<Key extends keyof Events>(type: Key, evt: Events[Key]) {
|
||||
let handlers = this.all!.get(type);
|
||||
if (handlers) {
|
||||
(handlers as EventHandlerList<Events[keyof Events]>)
|
||||
@ -104,14 +83,5 @@ export class Emitter<Events extends Record<EventType, unknown>> {
|
||||
handler(evt!);
|
||||
});
|
||||
}
|
||||
|
||||
handlers = this.all!.get('*');
|
||||
if (handlers) {
|
||||
(handlers as WildCardEventHandlerList<Events>)
|
||||
.slice()
|
||||
.map((handler) => {
|
||||
handler(type, evt!);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Emitter, EventType } from "./Emitter";
|
||||
import { Logger, LogLabel, LogStyle, LevelLogLabel } from "./Logger";
|
||||
import { Emitter } from "./Emitter";
|
||||
import { Logger, LogLabel, colorRadio, LevelLogLabel } from "./Logger";
|
||||
|
||||
/**
|
||||
* 自定义对象类型
|
||||
@ -50,6 +50,149 @@ type Depends<M extends Manager<AnyWXContext>> = {
|
||||
[x:string]: Modular<M, Depends<M>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 微信继承的函数
|
||||
*/
|
||||
class WXInstanceMethods<
|
||||
E extends IAnyTypeObject = IAnyTypeObject,
|
||||
W extends AnyWXContext = AnyWXContext
|
||||
>
|
||||
extends Emitter<E>
|
||||
implements InstanceMethods<W["data"]> {
|
||||
|
||||
public superContext: W;
|
||||
|
||||
public constructor(context: W) {
|
||||
super();
|
||||
this.superContext = context;
|
||||
}
|
||||
|
||||
public setData(data: Partial<W["data"]> & WechatMiniprogram.IAnyObject, callback?: () => void): void {
|
||||
return this.superContext.setData(data, callback);
|
||||
}
|
||||
|
||||
public hasBehavior(behavior: string): void {
|
||||
return this.superContext.hasBehavior(behavior);
|
||||
}
|
||||
|
||||
public triggerEvent<DetailType = any>(name: string, detail?: DetailType, options?: WechatMiniprogram.Component.TriggerEventOption): void {
|
||||
return this.superContext.triggerEvent(name, detail, options)
|
||||
}
|
||||
|
||||
public createSelectorQuery(): WechatMiniprogram.SelectorQuery {
|
||||
return this.superContext.createSelectorQuery();
|
||||
}
|
||||
|
||||
public createIntersectionObserver(options: WechatMiniprogram.CreateIntersectionObserverOption): WechatMiniprogram.IntersectionObserver {
|
||||
return this.superContext.createIntersectionObserver(options);
|
||||
}
|
||||
|
||||
public selectComponent(selector: string): WechatMiniprogram.Component.TrivialInstance {
|
||||
return this.superContext.selectComponent(selector);
|
||||
}
|
||||
|
||||
public selectAllComponents(selector: string): WechatMiniprogram.Component.TrivialInstance[] {
|
||||
return this.superContext.selectAllComponents(selector);
|
||||
}
|
||||
|
||||
public selectOwnerComponent(): WechatMiniprogram.Component.TrivialInstance {
|
||||
return this.superContext.selectOwnerComponent();
|
||||
}
|
||||
|
||||
public getRelationNodes(relationKey: string): WechatMiniprogram.Component.TrivialInstance[] {
|
||||
return this.superContext.getRelationNodes(relationKey);
|
||||
}
|
||||
|
||||
public groupSetData(callback?: () => void): void {
|
||||
return this.superContext.groupSetData(callback);
|
||||
}
|
||||
|
||||
public getTabBar(): WechatMiniprogram.Component.TrivialInstance {
|
||||
return this.superContext.getTabBar();
|
||||
}
|
||||
|
||||
public getPageId(): string {
|
||||
return this.superContext.getPageId();
|
||||
}
|
||||
|
||||
public animate(selector: string, keyFrames: WechatMiniprogram.Component.KeyFrame[], duration: number, callback?: () => void): void;
|
||||
public animate(selector: string, keyFrames: WechatMiniprogram.Component.ScrollTimelineKeyframe[], duration: number,
|
||||
scrollTimeline: WechatMiniprogram.Component.ScrollTimelineOption): void;
|
||||
public animate(selector: any, keyFrames: any, duration: any, scrollTimeline?: any): void {
|
||||
return this.superContext.animate(selector, keyFrames, duration, scrollTimeline);
|
||||
}
|
||||
|
||||
public clearAnimation(selector: string, callback: () => void): void;
|
||||
public clearAnimation(selector: string, options?: WechatMiniprogram.Component.ClearAnimationOptions, callback?: () => void): void;
|
||||
public clearAnimation(selector: any, options?: any, callback?: any): void {
|
||||
return this.superContext.clearAnimation(selector, options, callback);
|
||||
}
|
||||
|
||||
public getOpenerEventChannel(): WechatMiniprogram.EventChannel {
|
||||
return this.superContext.getOpenerEventChannel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信继承的属性
|
||||
*/
|
||||
class WXInstanceProperties<
|
||||
E extends IAnyTypeObject = IAnyTypeObject,
|
||||
W extends AnyWXContext = AnyWXContext
|
||||
>
|
||||
extends WXInstanceMethods<E, W>
|
||||
implements InstanceProperties {
|
||||
|
||||
public override superContext: W;
|
||||
|
||||
constructor(context: W) {
|
||||
super(context);
|
||||
this.superContext = context;
|
||||
}
|
||||
|
||||
public get is(): string { return this.superContext.is };
|
||||
|
||||
public get route(): string { return this.superContext.route };
|
||||
|
||||
public get options(): Record<string, string | undefined> { return this.superContext.options };
|
||||
}
|
||||
|
||||
class WXILifetime<
|
||||
E extends IAnyTypeObject = IAnyTypeObject,
|
||||
W extends AnyWXContext = AnyWXContext
|
||||
>
|
||||
extends WXInstanceProperties<E, W>
|
||||
implements ILifetime {
|
||||
|
||||
public onLoad(query: Record<string, string | undefined>): void | Promise<void> {};
|
||||
|
||||
public onShow(): void | Promise<void> {};
|
||||
|
||||
public onReady(): void | Promise<void> {};
|
||||
|
||||
public onHide(): void | Promise<void> {};
|
||||
|
||||
public onUnload(): void | Promise<void> {};
|
||||
|
||||
public onPullDownRefresh(): void | Promise<void> {};
|
||||
|
||||
public onReachBottom(): void | Promise<void> {};
|
||||
|
||||
public onShareAppMessage(options: WechatMiniprogram.Page.IShareAppMessageOption): void | WechatMiniprogram.Page.ICustomShareContent {};
|
||||
|
||||
public onShareTimeline(): void | WechatMiniprogram.Page.ICustomTimelineContent {};
|
||||
|
||||
public onPageScroll(options: WechatMiniprogram.Page.IPageScrollOption): void | Promise<void> {};
|
||||
|
||||
public onTabItemTap(options: WechatMiniprogram.Page.ITabItemTapOption): void | Promise<void> {};
|
||||
|
||||
public onResize(options: WechatMiniprogram.Page.IResizeOption): void | Promise<void> {};
|
||||
|
||||
public onAddToFavorites(options: WechatMiniprogram.Page.IAddToFavoritesOption): WechatMiniprogram.Page.IAddToFavoritesContent {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面模组
|
||||
* @template M 所属 Manager
|
||||
@ -60,9 +203,10 @@ type Depends<M extends Manager<AnyWXContext>> = {
|
||||
class Modular<
|
||||
M extends Manager<AnyWXContext> = Manager<AnyWXContext>,
|
||||
DEP extends Depends<M> = Depends<M>,
|
||||
E extends Record<EventType, unknown> = Record<EventType, unknown>,
|
||||
TD extends IAnyTypeObject = IAnyTypeObject>
|
||||
extends Emitter<E>
|
||||
E extends IAnyTypeObject = IAnyTypeObject,
|
||||
TD extends IAnyTypeObject = IAnyTypeObject
|
||||
>
|
||||
extends WXILifetime<E, M["context"]>
|
||||
implements WXContext<TD, IAnyTypeObject> {
|
||||
|
||||
// [x:string]: any;
|
||||
@ -95,7 +239,7 @@ implements WXContext<TD, IAnyTypeObject> {
|
||||
public functionList:Set<string>;
|
||||
|
||||
/**
|
||||
* 函数使用的参数列表
|
||||
* 模组使用的参数列表
|
||||
*/
|
||||
public paramList:Set<string>;
|
||||
|
||||
@ -103,13 +247,6 @@ implements WXContext<TD, IAnyTypeObject> {
|
||||
* 命名空间
|
||||
*/
|
||||
public nameSpace:string;
|
||||
|
||||
// 映射主上下文属性
|
||||
public get is():string { return this.context.is };
|
||||
public get route():string { return this.context.route };
|
||||
public get options():Record<string, string | undefined> {
|
||||
return this.context.options;
|
||||
};
|
||||
|
||||
/**
|
||||
* 一旦被类被构造,整个页面的全部申明周期将
|
||||
@ -120,7 +257,7 @@ implements WXContext<TD, IAnyTypeObject> {
|
||||
*/
|
||||
public constructor(manager:M, nameSpace:string, depend?: DEP) {
|
||||
|
||||
super();
|
||||
super(manager.context);
|
||||
|
||||
// 保存微信上下文
|
||||
this.manager = manager;
|
||||
@ -134,7 +271,7 @@ implements WXContext<TD, IAnyTypeObject> {
|
||||
this.nameSpace = nameSpace;
|
||||
}
|
||||
|
||||
public setData(data:Partial<TD>, callback?: () => void):void {
|
||||
public override setData(data:Partial<TD>, callback?: () => void):void {
|
||||
|
||||
if(this.data === void 0) {
|
||||
this.data = {} as TD;
|
||||
@ -162,76 +299,6 @@ implements WXContext<TD, IAnyTypeObject> {
|
||||
(this.context as IAnyTypeObject)
|
||||
[`${ this.nameSpace }$${ name }`] = fn.bind(this);
|
||||
}
|
||||
|
||||
//#region 映射微信的继承函数
|
||||
|
||||
public hasBehavior(behavior: string): void {
|
||||
return this.context.hasBehavior(behavior);
|
||||
}
|
||||
|
||||
public triggerEvent<DetailType>(name: string, detail?: DetailType,
|
||||
options?: WechatMiniprogram.Component.TriggerEventOption): void {
|
||||
return this.context.triggerEvent<DetailType>(name, detail, options);
|
||||
}
|
||||
|
||||
public createSelectorQuery(): WechatMiniprogram.SelectorQuery {
|
||||
return this.context.createSelectorQuery();
|
||||
}
|
||||
|
||||
public createIntersectionObserver(options: WechatMiniprogram.CreateIntersectionObserverOption):
|
||||
WechatMiniprogram.IntersectionObserver {
|
||||
return this.context.createIntersectionObserver(options);
|
||||
}
|
||||
|
||||
public selectComponent(selector: string): WechatMiniprogram.Component.TrivialInstance {
|
||||
return this.context.selectComponent(selector);
|
||||
}
|
||||
|
||||
public selectAllComponents(selector: string): WechatMiniprogram.Component.TrivialInstance[] {
|
||||
return this.context.selectAllComponents(selector);
|
||||
}
|
||||
|
||||
public selectOwnerComponent(): WechatMiniprogram.Component.TrivialInstance {
|
||||
return this.context.selectOwnerComponent();
|
||||
}
|
||||
|
||||
public getRelationNodes(relationKey: string): WechatMiniprogram.Component.TrivialInstance[] {
|
||||
return this.context.getRelationNodes(relationKey);
|
||||
}
|
||||
|
||||
public groupSetData(callback?: () => void): void {
|
||||
return this.context.groupSetData(callback);
|
||||
}
|
||||
|
||||
public getTabBar(): WechatMiniprogram.Component.TrivialInstance {
|
||||
return this.context.getTabBar();
|
||||
}
|
||||
|
||||
public getPageId(): string {
|
||||
return this.context.getPageId();
|
||||
}
|
||||
|
||||
public animate(selector: string, keyFrames: WechatMiniprogram.Component.KeyFrame[],
|
||||
duration: number, callback?: () => void): void;
|
||||
public animate(selector: string, keyFrames: WechatMiniprogram.Component.ScrollTimelineKeyframe[],
|
||||
duration: number, scrollTimeline: WechatMiniprogram.Component.ScrollTimelineOption): void;
|
||||
public animate(selector: any, keyFrames: any, duration: any, scrollTimeline?: any): void {
|
||||
return this.context.animate(selector, keyFrames, duration, scrollTimeline);
|
||||
}
|
||||
|
||||
public clearAnimation(selector: string, callback: () => void): void;
|
||||
public clearAnimation(selector: string, options?: WechatMiniprogram.Component.ClearAnimationOptions,
|
||||
callback?: () => void): void;
|
||||
public clearAnimation(selector: any, options?: any, callback?: any): void {
|
||||
return this.context.clearAnimation(selector, options, callback);
|
||||
}
|
||||
|
||||
public getOpenerEventChannel(): WechatMiniprogram.EventChannel {
|
||||
return this.context.getOpenerEventChannel();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,19 +311,9 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
|
||||
* 微信生命周期
|
||||
*/
|
||||
static readonly WxLifeCycle:(keyof ILifetime)[] = [
|
||||
"onShow",
|
||||
"onReady",
|
||||
"onHide",
|
||||
"onUnload",
|
||||
"onPullDownRefresh",
|
||||
"onReachBottom",
|
||||
"onShareAppMessage",
|
||||
"onShareTimeline",
|
||||
"onAddToFavorites",
|
||||
"onPageScroll",
|
||||
"onResize",
|
||||
"onTabItemTap"
|
||||
]
|
||||
"onShow", "onReady", "onHide", "onUnload", "onPullDownRefresh", "onReachBottom",
|
||||
"onShareAppMessage", "onShareTimeline","onAddToFavorites","onPageScroll", "onResize", "onTabItemTap"
|
||||
];
|
||||
|
||||
/**
|
||||
* 保存页面上下文
|
||||
@ -288,9 +345,10 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
|
||||
* @param depend 模块依赖
|
||||
* @returns 模块实例
|
||||
*/
|
||||
public addModule<DEP extends Depends<this>, M extends Modular<this, DEP>>
|
||||
(mode: new (manager:Manager<WXC>, nameSpace:string, depend?:DEP) => M,
|
||||
nameSpace:string, depend?:DEP):M {
|
||||
public addModule<DEP extends Depends<this>, M extends Modular<this, DEP>> (
|
||||
mode: new (manager:this, nameSpace:string, depend?:DEP) => M,
|
||||
nameSpace:string, depend?:DEP
|
||||
):M {
|
||||
let mod = new mode(this, nameSpace, depend);
|
||||
this.modules.push(mod);
|
||||
return mod;
|
||||
@ -300,14 +358,14 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
|
||||
* 创建指定生命周期的钩子
|
||||
* @param key 生命周期键值
|
||||
*/
|
||||
public creatHooks(key:keyof ILifetime):(...arg: any[]) => Promise<any> {
|
||||
return async (...arg: any[]) => {
|
||||
public creatHooks<Key extends keyof ILifetime>(key: Key): ILifetime[Key] {
|
||||
let hook = (async (...arg: any[]) => {
|
||||
|
||||
let hooks:Promise<any>[] = [];
|
||||
|
||||
for(let i = 0; i < this.modules.length; i++) {
|
||||
|
||||
let fn:Function = (this.modules[i] as IAnyTypeObject)[key];
|
||||
let fn:Function = this.modules[i][key];
|
||||
|
||||
if(fn === void 0) continue;
|
||||
let res: Promise<any> | any = fn.apply(this.modules[i], arg);
|
||||
@ -340,7 +398,10 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
|
||||
}
|
||||
|
||||
return Promise.all(hooks);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: 此处为,关键位置,容易出错,请再次检查
|
||||
return (hook as any);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -383,7 +444,7 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
|
||||
this.context.setData(this.context.data);
|
||||
|
||||
// 调用全部模块的 onLoad 周期
|
||||
let res = this.creatHooks("onLoad")(query as any);
|
||||
let res = this.creatHooks("onLoad")(query);
|
||||
|
||||
// 打印每个模块的键值对使用情况
|
||||
for(let i = 0; i < this.modules.length; i++) {
|
||||
@ -411,9 +472,9 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
|
||||
/**
|
||||
* 模块被添加时的标签
|
||||
*/
|
||||
public static readonly AddModuleLabel = new LogLabel("addModule",
|
||||
new LogStyle().setBorder("4px", `1px solid #8600FF`)
|
||||
.setColor("#FF00FF", "rgba(54, 0, 255, .2)").setBlank("0 5px")
|
||||
public static readonly AddModuleLabel = new LogLabel(
|
||||
"addModule",
|
||||
colorRadio(54, 0, 255)
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,13 @@
|
||||
import { Logger, LogLabel, LevelLogLabel, colorRadio } from "./Logger";
|
||||
|
||||
interface IAppStorageParam {
|
||||
|
||||
/**
|
||||
* storage 缓存
|
||||
*/
|
||||
storage: Map<string, Storage<IStorageData>>
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@ -81,7 +89,8 @@ type IStorageData = {
|
||||
* 1. 该类封装了 wxStorage 操作
|
||||
* 2. 全异步获取无阻塞
|
||||
* 3. 使用数据缓缓冲区,优化高频存取
|
||||
* 4.
|
||||
* 4. 如果全局范围内已存在莫键值 storage 的实例
|
||||
* 则此实例将链接到已存在实例
|
||||
*/
|
||||
class Storage<T extends IStorageData> {
|
||||
|
||||
@ -103,18 +112,41 @@ class Storage<T extends IStorageData> {
|
||||
/**
|
||||
* 缓存数据
|
||||
*/
|
||||
private cache:T;
|
||||
private _cache: T = {} as T;
|
||||
private set cache(data: T) {
|
||||
if (this.cacheStorage) {
|
||||
for (const key in data) {
|
||||
this.cacheStorage.cache[key] = data[key];
|
||||
}
|
||||
} else {
|
||||
for (const key in data) {
|
||||
this._cache[key] = data[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
private get cache():T {
|
||||
if (this.cacheStorage) return this.cacheStorage.cache;
|
||||
else return this._cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* cache 到 storage 等待列表
|
||||
*/
|
||||
private saveWaiter:Waiter = new Waiter();
|
||||
|
||||
/**
|
||||
* 缓存对象
|
||||
*/
|
||||
private cacheStorage:Storage<T> | undefined;
|
||||
|
||||
/**
|
||||
* 将数据从 cache 同步到 storage
|
||||
*/
|
||||
public async save():Promise<void> {
|
||||
|
||||
// 如果存在链接的实例
|
||||
if (this.cacheStorage) return this.cacheStorage.save();
|
||||
|
||||
// 如果没有开始存储
|
||||
// 发起一次异步读取
|
||||
if(this.saveWaiter.state === StorageState.DONE)
|
||||
@ -150,6 +182,24 @@ class Storage<T extends IStorageData> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 在缓存中搜索此键值的实例
|
||||
*/
|
||||
private findStorageCache(): boolean {
|
||||
let { storage: storageMap } = getApp<IAppStorageParam>();
|
||||
|
||||
// 查找缓存
|
||||
let storage = storageMap.get(this.key);
|
||||
if (storage) {
|
||||
this.cacheStorage = storage as Storage<T>;
|
||||
return true;
|
||||
};
|
||||
|
||||
// 缓存此实例
|
||||
storageMap.set(this.key, this);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置全部数据
|
||||
*/
|
||||
@ -164,13 +214,25 @@ class Storage<T extends IStorageData> {
|
||||
/**
|
||||
* @param defaultData 键值默认数据
|
||||
*/
|
||||
public constructor(key:string, defaultData:T) {
|
||||
public constructor(key:string, defaultData?:T) {
|
||||
this.key = key;
|
||||
this.defaultData = defaultData;
|
||||
this.defaultData = defaultData ?? {} as T;
|
||||
this.StorageLogLabel = new LogLabel(
|
||||
`Storage:${ this.key }`, colorRadio(34, 230, 258)
|
||||
);
|
||||
|
||||
// 如果已找到其他实力,将此实例链接到目标实例
|
||||
if (this.findStorageCache()) {
|
||||
|
||||
// 设置默认值
|
||||
for (const key in this.defaultData) {
|
||||
if (this.cache[key] === void 0) {
|
||||
this.set(key, this.defaultData[key]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// 读取数据到缓存
|
||||
this.cache = wx.getStorageSync<T>(this.key);
|
||||
|
||||
@ -205,4 +267,4 @@ class Storage<T extends IStorageData> {
|
||||
}
|
||||
|
||||
export default Storage;
|
||||
export { Storage, StorageState, Waiter };
|
||||
export { Storage, StorageState, Waiter, IAppStorageParam, IStorageData };
|
@ -1,205 +0,0 @@
|
||||
// import { Logger } from "../logger/Logger";
|
||||
import { LogStyle, LogLabel } from "./Logger";
|
||||
|
||||
/**
|
||||
* 测试结果
|
||||
*/
|
||||
class TestResult {
|
||||
|
||||
/**
|
||||
* 用例名称
|
||||
*/
|
||||
public caseName:string;
|
||||
|
||||
/**
|
||||
* 测试结果
|
||||
*/
|
||||
public result:boolean;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
*/
|
||||
public message:string;
|
||||
|
||||
/**
|
||||
* 附加消息
|
||||
*/
|
||||
public attach:string;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param caseName 用例名称
|
||||
*/
|
||||
constructor(caseName:string) {
|
||||
this.caseName = caseName;
|
||||
this.result = false;
|
||||
this.message = "";
|
||||
this.attach = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置结果
|
||||
*/
|
||||
public setResult(result:boolean, message?:string, attach?:string) {
|
||||
this.result = result;
|
||||
this.message = message ?? (result ? "success!" : "failed!");
|
||||
this.attach = attach ?? this.attach;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试函数结构
|
||||
*/
|
||||
type TestFunction = () => TestResult | Promise<TestResult>;
|
||||
|
||||
/**
|
||||
* 收集测试函数结构
|
||||
*/
|
||||
class CaseCollect {
|
||||
|
||||
/**
|
||||
* 用例键名
|
||||
*/
|
||||
public key:string;
|
||||
|
||||
/**
|
||||
* 用例测试函数
|
||||
*/
|
||||
public caseFunction:TestFunction;
|
||||
|
||||
/**
|
||||
* 测试结果
|
||||
*/
|
||||
result: Promise<TestResult> | undefined;
|
||||
|
||||
/**
|
||||
* @param key 测试用例键名
|
||||
* @param caseFunction 测试函数
|
||||
*/
|
||||
public constructor(key:string, caseFunction:TestFunction) {
|
||||
this.key = key;
|
||||
this.caseFunction = caseFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行测试用例
|
||||
*/
|
||||
public async runTestCase():Promise<CaseCollect> {
|
||||
|
||||
let result = this.caseFunction();
|
||||
|
||||
if(result instanceof Promise) {
|
||||
this.result = result;
|
||||
} else {
|
||||
this.result = Promise.resolve(result);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static readonly baseStyle = new LogStyle().setBlank();
|
||||
|
||||
public static readonly successLabel:LogLabel = new LogLabel("√",
|
||||
new LogStyle().setBlank("0 4px").setBorder("1000px", "1px solid green")
|
||||
);
|
||||
|
||||
/**
|
||||
* 打印结果
|
||||
* @param current 当前进度
|
||||
* @param total 总进度
|
||||
*/
|
||||
public printResult(current?:number, total?:number) {
|
||||
|
||||
// 如果测试没有运行,先运行它
|
||||
if(this.result === void 0) this.runTestCase();
|
||||
|
||||
this.result?.then((res) => {
|
||||
|
||||
if(res.result) {
|
||||
console.log(
|
||||
`%c√%c %c1/1%c %c${ this.key }%c ` + res.message,
|
||||
"padding:0 4px; border-radius:1000px; border:1px solid green; color:green",
|
||||
"", "padding:0 4px; border-radius:4px; border:1px solid green; color:green",
|
||||
"", "padding:0 4px; border-radius:4px; border:1px solid #979797; color:#979797",
|
||||
""
|
||||
)
|
||||
} else {
|
||||
console.log(
|
||||
`%c√%c %c1/1%c %c${ this.key }%c ` + res.message,
|
||||
"padding:0 4px; border-radius:1000px; border:1px solid red; color:red",
|
||||
"", "padding:0 4px; border-radius:4px; border:1px solid red; color:red",
|
||||
"", "padding:0 4px; border-radius:4px; border:1px solid #979797; color:#979797",
|
||||
""
|
||||
)
|
||||
}
|
||||
console.log(res)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集测试用例
|
||||
* @param testCaseClass 测试用例表
|
||||
*/
|
||||
public static collectCase(testCaseClass:ITestCase):CaseCollect[] {
|
||||
|
||||
// 获取静态方法 key
|
||||
let key = Object.getOwnPropertyNames(testCaseClass);
|
||||
|
||||
// 过滤掉通用的方法和属性
|
||||
key = key.filter((key) => !/(length|name|prototype)/.test(key) );
|
||||
|
||||
// 生成 CaseCollect
|
||||
let caseCollect = [];
|
||||
|
||||
for (let i = 0; i < key.length; i++) {
|
||||
caseCollect.push(new CaseCollect(key[i], testCaseClass[key[i]]))
|
||||
}
|
||||
|
||||
return caseCollect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行测试样例
|
||||
*/
|
||||
public static async runCollectCase(cases:CaseCollect[]):Promise<CaseCollect[]> {
|
||||
|
||||
let running:Promise<CaseCollect>[] = [];
|
||||
|
||||
for(let i = 0; i < cases.length; i++) {
|
||||
running.push(cases[i].runTestCase());
|
||||
}
|
||||
|
||||
return Promise.all(running);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动单元测试
|
||||
*/
|
||||
public static runUnitTest(testCaseClass:ITestCase) {
|
||||
|
||||
let caseCollect = this.collectCase(testCaseClass);
|
||||
|
||||
CaseCollect.runCollectCase(caseCollect).then((caseCollect:CaseCollect[]) => {
|
||||
|
||||
for(let i = 0; i < caseCollect.length; i++) {
|
||||
caseCollect[i].printResult()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用例接口
|
||||
*/
|
||||
interface ITestCase {
|
||||
|
||||
/**
|
||||
* 测试用例函数
|
||||
*/
|
||||
[key:string]:TestFunction;
|
||||
}
|
||||
|
||||
export default ITestCase;
|
||||
export { ITestCase, TestResult, TestFunction, CaseCollect };
|
@ -4,7 +4,7 @@ import { Logger, LogLabel, LevelLogLabel, LifeCycleLogLabel, NormalStyle } from
|
||||
/**
|
||||
* 在 UI 中显示的数据
|
||||
*/
|
||||
type DisplayData = {
|
||||
interface IDisplayData {
|
||||
|
||||
/**
|
||||
* 显示内容
|
||||
@ -20,8 +20,8 @@ type DisplayData = {
|
||||
/**
|
||||
* 模组事件
|
||||
*/
|
||||
type StatusBarEvent = {
|
||||
m: DisplayData
|
||||
interface StatusBarEvent {
|
||||
m: IDisplayData
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -8,13 +8,22 @@ import { Storage } from "../../core/Storage";
|
||||
class TestCore<M extends Manager> extends Modular<M>
|
||||
implements Partial<ILifetime> {
|
||||
|
||||
public onLoad() {
|
||||
public override onLoad() {
|
||||
|
||||
let s = new Storage("test", {
|
||||
a: new Date(),
|
||||
be: 2
|
||||
});
|
||||
|
||||
let s2 = new Storage("test", {
|
||||
be: 1,
|
||||
aa: "abc"
|
||||
});
|
||||
|
||||
s2.set("be", 4);
|
||||
|
||||
console.log(s, s2);
|
||||
|
||||
setTimeout(() => {
|
||||
s.set("be", 12);
|
||||
}, 1000)
|
||||
@ -55,9 +64,7 @@ implements Partial<ILifetime> {
|
||||
}
|
||||
}).request().wait({
|
||||
success: (d) => console.log(d)
|
||||
}).wait({
|
||||
success: (d) => console.log(d)
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user