import { Compiler } from "./compiler"; import { CssManager } from "./cssmanager"; import { VirtualDom } from "./virtualdom"; import { Model } from "./model"; import { ModuleFactory } from "./modulefactory"; import { ObjectManager } from "./objectmanager"; import { Renderer } from "./renderer"; import { Util } from "./util"; import { DiffTool } from "./difftool"; import { ModelManager } from "./modelmanager"; import { EModuleState, IRenderedDom } from "./types"; import { EventFactory } from "./eventfactory"; /** * 模块类 * 模块方法说明:模版内使用的方法,包括事件,都直接在模块内定义 * 方法this:指向module实例 * 事件参数: model(当前按钮对应model),dom(事件对应虚拟dom),eventObj(事件对象),e(实际触发的html event) * 表达式方法:参数按照表达式方式给定即可 */ export class Module { /** * 模块id(全局唯一) */ public id: number; /** * 模型,代理过的data */ public model:Model; /** * 子模块类集合,模版中引用的模块类需要声明 * 如果类已经通过registModule注册过,这里不再需要定义,只需import即可 */ public modules: any; /** * 父模块通过dom节点传递的属性 */ public props:any; /** * 编译后的dom树 */ public originTree:VirtualDom; /** * 渲染树 */ public renderTree: IRenderedDom; /** * 父模块 id */ public parentId: number; /** * 子模块id数组 */ public children: Array = []; /** * 模块状态 */ public state: EModuleState; /** * 放置模块的容器 */ private container: HTMLElement; /** * 后置渲染序列 */ private preRenderOps:any[] = []; /** * 后置渲染序列 */ private postRenderOps:any[] = []; /** * 对象管理器,用于管理虚拟dom节点、指令、表达式、事件对象及其运行时参数 */ public objectManager:ObjectManager; /** * 事件工厂 */ public eventFactory:EventFactory; /** * 更改model的map,用于记录增量渲染时更改的model */ public changedModelMap:Map; /** * 用于保存每个key对应的html node */ private keyNodeMap:Map = new Map(); /** * 用户自定义key htmlelement映射 */ private keyElementMap:Map = new Map(); /** * key virtualdom map */ private keyVDomMap:Map = new Map(); /** * 不允许加入渲染队列标志,在renderdom前设置,避免render前修改数据引发二次渲染 */ public dontAddToRender:boolean; /** * 来源dom,子模块对应dom */ public srcDom:IRenderedDom; /** * 生成dom时的keyid,每次编译置0 */ private domKeyId:number; /** * 旧模版串 */ private oldTemplate:string; /** * 源节点对应的html element */ private srcElement:Node; /** * 构造器 */ constructor() { this.id = Util.genId(); this.objectManager = new ObjectManager(this); this.changedModelMap = new Map(); this.eventFactory = new EventFactory(this); //加入模块工厂 ModuleFactory.add(this); } /** * 初始化 */ public init() { // 设置状态为初始化 this.state = EModuleState.INITED; //初始化model this.model = new Model(this.data()||{} , this); //注册子模块 if(this.modules && Array.isArray(this.modules)){ for (let cls of this.modules) { ModuleFactory.addClass(cls); } delete this.modules; } } /** * 模版串方法,使用时重载 * @param props props对象,在模版容器dom中进行配置,从父模块传入 * @returns 模版串 */ public template(props?:any):string{ return null; } /** * 数据方法,使用时重载 * @returns model数据 */ public data():any{ return {}; } /** * 模型渲染 */ public render(): boolean { if(this.state === EModuleState.UNACTIVE){ return; } this.dontAddToRender = true; //检测模版并编译 let templateStr = this.template(this.props); if(templateStr !== this.oldTemplate){ this.oldTemplate = templateStr; this.compile(); } //不存在,不渲染 if(!this.originTree){ return; } //执行前置方法 this.doRenderOps(0); //渲染前事件返回true,则不进行渲染 if(this.doModuleEvent('onBeforeRender')){ this.dontAddToRender = false; return; } if (!this.renderTree) { this.doFirstRender(); } else { //增量渲染 //执行每次渲染前事件 if (this.model) { let oldTree = this.renderTree; this.renderTree = Renderer.renderDom(this,this.originTree,this.model); this.doModuleEvent('onBeforeRenderToHtml'); let changeDoms = []; // 比较节点 DiffTool.compare(this.renderTree,oldTree, changeDoms); //执行更改 if(changeDoms.length>0){ Renderer.handleChangedDoms(this,changeDoms); } } } //设置已渲染状态 this.state = EModuleState.RENDERED; //执行后置方法 this.doRenderOps(1); //执行每次渲染后事件 this.doModuleEvent('onRender'); this.changedModelMap.clear(); this.dontAddToRender = false; } /** * 执行首次渲染 * @param root 根虚拟dom */ private doFirstRender() { this.doModuleEvent('onBeforeFirstRender'); //渲染树 this.renderTree = Renderer.renderDom(this,this.originTree,this.model); this.doModuleEvent('onBeforeFirstRenderToHTML'); //渲染为html element let el:any = Renderer.renderToHtml(this,this.renderTree,null,true); if(this.srcDom){ //子模块 this.srcElement = this.getParent().getNode(this.srcDom.key); this.exchange(); }else if(this.container){ //路由 this.container.appendChild(el); } //执行首次渲染后事件 this.doModuleEvent('onFirstRender'); } /** * 添加子模块 * @param module 模块id或模块 */ public addChild(module: number|Module) { let mid; if(typeof module === 'number'){ mid = module; module = ModuleFactory.get(mid); }else{ mid = module.id; } if (!this.children.includes(mid)) { this.children.push(mid); module.parentId = this.id; //首次添加,激活 module.active(); } } /** * 移除子模块 * @param module 子模块 */ public removeChild(module: Module) { let ind=this.children.indexOf(module.id); if (ind !== -1) { module.unactive(); this.children.splice(ind,1); } } /** * 激活模块(添加到渲染器) * @param type 0 手动, 1父节点setProps激活,默认0 */ public active(type?:number) { //如果为手动active,srcdom存在且不在renderTree中,则不active if(!type && this.srcDom){ const pm = this.getParent(); if(pm && !pm.findRenderedDom(this.srcDom.key)){ return; } } this.state = EModuleState.INITED; Renderer.add(this); } /** * 取消激活 */ public unactive() { if (ModuleFactory.getMain() === this) { return; } this.doModuleEvent('beforeUnActive'); //设置状态 this.state = EModuleState.UNACTIVE; if(this.renderTree){ if(this.container){ const el = this.getNode(this.renderTree.key); if(el){ this.container.removeChild(el); } }else{ this.exchange(1); } } // delete this.srcDom; //删除渲染树 delete this.renderTree; // 清理dom key map this.keyNodeMap.clear(); this.keyElementMap.clear(); this.keyVDomMap.clear(); //清空event factory this.eventFactory = new EventFactory(this); //unactive事件 this.doModuleEvent('unActive'); //处理子模块 if(this.children){ //处理子模块 for(let id of this.children){ let m = ModuleFactory.get(id); if(m){ m.unactive(); } } } } /** * 获取父模块 * @returns 父模块 */ public getParent(): Module { if (!this.parentId) { return; } return ModuleFactory.get(this.parentId); } /** * 执行模块事件 * @param eventName 事件名 * @returns 执行结果,各事件返回值如下: * onBeforeRender:如果为true,表示不进行渲染 */ private doModuleEvent(eventName: string):boolean{ let foo = this[eventName]; if(foo && typeof foo==='function'){ return foo.apply(this,[this.model]); } } /** * 获取模块方法 * @param name 方法名 * @returns 方法 */ public getMethod(name: string): Function { return this[name]; } /** * 设置渲染容器 * @param el 容器 */ public setContainer(el:HTMLElement){ this.container = el; } /** * 调用方法 * @param methodName 方法名 */ public invokeMethod(methodName: string,arg1?:any,arg2?:any,arg3?:any) { let foo; let m:Module = this; //方法级联向上找,找到第一个则返回 while(m){ foo = m[methodName]; if(foo){ break; } m = m.getParent(); } if (foo && typeof foo === 'function') { let args = []; for(let i=1;i