zero-mvc
Version:
zeromvc是一种mvc设计模式。主要意义在于分离视图代码、逻辑代码和数据处理代码。
238 lines (231 loc) • 7.77 kB
text/typescript
/**
* 蓝面包(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()
}
}