UNPKG

zero-mvc

Version:

zeromvc是一种mvc设计模式。主要意义在于分离视图代码、逻辑代码和数据处理代码。

238 lines (231 loc) 7.77 kB
/** * 蓝面包(wc24@qq.com) * 20211224 * 精简mvc设计模块 * https://www.npmjs.com/package/zero-mvc */ type CommandType<T extends Command> = new (...args: any[]) => T type ViewType = new () => View export class Command { } export class Zeromvc { protected pool: Map<any, any> = new Map() protected showViewList: (ViewType)[] = []; static zeromvc: Zeromvc constructor() { if (Zeromvc.zeromvc != null) { Zeromvc.zeromvc.clear() } Zeromvc.zeromvc = this } async emit(command: Command): Promise<void> async emit(command: string, ...args: any[]): Promise<void> async emit(command: any, ...args: any[]) { if (command instanceof Command) { let callbacks = this.pool.get(command.constructor) if (callbacks != null) { for (let index = 0; index < callbacks.length; index++) { const element = callbacks[index]; await element(command) } } } else { let callbacks = this.pool.get(command) if (callbacks != null) { for (let index = 0; index < callbacks.length; index++) { const element = callbacks[index]; await element(...args) } } } } on<T extends Command>(command: string, callback: (command: string) => Promise<void>): void on<T extends Command>(commandType: CommandType<T>, callback: (value: T) => Promise<void>): void on(command: any, callback: any) { let callbacks = this.pool.get(command) if (callbacks == null) { callbacks = []; this.pool.set(command, callbacks) } callbacks.push(callback); } private instanceMap: WeakMap<any, any> = new WeakMap() getSingle<T>(singleType: (new (zero: Zeromvc) => T) | (new () => T)): T { let single: any if (this.instanceMap.has(singleType)) { single = this.instanceMap.get(singleType)! } else { single = new singleType(this) if (single._init != null) { single._init() } this.instanceMap.set(singleType, single) } return <T>single } getModel<U extends Model>(modelType: new () => U): U { return Model.getViewProxy(Zeromvc.zeromvc.getSingle(modelType)) } clear() { this.pool.clear() } } /** * 视图代码组织容器 */ export class View { private bindList: { model: Model, target: any, prefix: string, callback: (key: string, value: any) => void }[] = []; show() { this.bindList.forEach((item) => { Model.link(item.model, item.callback) }) } hide() { this.bindList.forEach((item) => { Model.unLink(item.model, item.callback) }) } getModel<U extends Model>(modelType: new () => U): U { return Model.getViewProxy(Zeromvc.zeromvc.getSingle(modelType)) } async emit(command: Command): Promise<void> async emit(command: string, ...args: any[]): Promise<void> async emit(command: any, ...args: any[]) { return Zeromvc.zeromvc.emit(command, ...args) } bind(model: Model, target: any, prefix = "") { this.bindList.push({ model: model, target: target, prefix: prefix, callback: (key: string, value: any) => { const fn: Function = target[prefix + key] if (fn != null && typeof fn == "function") { fn.apply(target, [value]) } } }) } } /** * 逻辑代码组织容器 */ export class Control { getModel<T extends Model>(modelType: new () => T): T { return Model.getControlProxy(Zeromvc.zeromvc.getSingle(modelType)) } async emit(command: Command): Promise<void> async emit(command: string, ...args: any[]): Promise<void> async emit(command: any, ...args: any[]) { return Zeromvc.zeromvc.emit(command, ...args) } on<T extends Command>(command: string, callback: (...args: any[]) => void): void on<T extends Command>(commandType: CommandType<T>, callback: (value: T) => void): void on(command: any, callback: any) { Zeromvc.zeromvc.on(command, callback) } } class ModelTick { tickList: Model[] = [] constructor(public tickTime = 16) { setInterval(() => { this.tickList.forEach(element => { const keyPool = Model.tick(element) for (const key in keyPool) { const value = keyPool[key]; element.update(key, value) } }); this.tickList = [] }, this.tickTime) } } /** * 数据代码组织容器 */ export class BaseModel { static link(model: BaseModel, value: Function): void { if (!model.pool.has(value)) { model.pool.add(value) } } static unLink(model: BaseModel, value: Function) { if (model.pool.has(value)) { model.pool.delete(value) } } protected start() { } private pool: Set<Function> = new Set() update(key: string, ...args: any[]) { this.pool.forEach((item) => { item(key, ...args) }) } _init() { this.start() } } /** * 数据代码组织容器 */ export class Model extends BaseModel { static modelTick: ModelTick = new ModelTick() static getControlProxy<T extends Model>(value: T): T { return value.controlProxy as T } static getViewProxy<T extends Model>(value: T): T { return value.viewProxy as T } static tick<T extends Model>(value: T): any { const out = value.updateKeyPool value.updateKeyPool = {} return out } protected start() { } protected controlProxy!: this protected viewProxy!: this protected updateKeyPool: any = {} _init() { this.controlProxy = new Proxy<any>(this, { set: (target: any, key: PropertyKey, value: any, receiver: any): boolean => { if ((target as any)[key] != value) { (target as any)[key] = value if (typeof key == "string" && key.charAt(0) != "_") { this.updateKeyPool[key] = value Model.modelTick.tickList.push(target) } } return true }, get: (target: any, key: PropertyKey) => { const value = target[key] if (typeof value == "function" && typeof key == "string" && key.charAt(0) != "_") { return (...args: any[]) => { value.apply(target, args) this.updateKeyPool[key] = null Model.modelTick.tickList.push(target) } } else { return value; } } }) this.viewProxy = new Proxy<any>(this, { set: (target: any, key: PropertyKey, value: any, receiver: any): boolean => { (target as any)[key] = value throw new Error("在视图(View)中里不能直接修改模型(Model)的值") }, get: (target: any, key: PropertyKey) => { const value = target[key] if (typeof value == "function") { throw new Error("在视图(View)中里不能直接调用模型(Model)的方法") } else { return value; } } }) super._init() } }