diff --git a/packages/core/src/App.ts b/packages/core/src/App.ts index 2037140a..4ff3db7e 100644 --- a/packages/core/src/App.ts +++ b/packages/core/src/App.ts @@ -18,7 +18,7 @@ import { EventEmitter } from 'events'; -import type { Id, MApp } from '@tmagic/schema'; +import type { EventItemConfig, Id, MApp } from '@tmagic/schema'; import Env from './Env'; import { @@ -40,17 +40,25 @@ interface AppOptionsConfig { transformStyle?: (style: Record) => Record; } +interface EventCache { + eventConfig: EventItemConfig; + fromCpt: any; + args: any[]; +} + class App extends EventEmitter { - env; + public env; - pages = new Map(); + public pages = new Map(); - page: Page | undefined; + public page: Page | undefined; - platform = 'mobile'; - jsEngine = 'browser'; + public platform = 'mobile'; + public jsEngine = 'browser'; - components = new Map(); + public components = new Map(); + + public eventQueueMap: Record = {}; constructor(options: AppOptionsConfig) { super(); @@ -89,7 +97,7 @@ class App extends EventEmitter { * @param style Object * @returns Object */ - transformStyle(style: Record | string) { + public transformStyle(style: Record | string) { if (!style) { return {}; } @@ -132,7 +140,7 @@ class App extends EventEmitter { * @param config dsl跟节点 * @param curPage 当前页面id */ - setConfig(config: MApp, curPage?: Id) { + public setConfig(config: MApp, curPage?: Id) { this.pages = new Map(); config.items?.forEach((page) => { @@ -140,6 +148,7 @@ class App extends EventEmitter { page.id, new Page({ config: page, + app: this, }), ); }); @@ -147,7 +156,7 @@ class App extends EventEmitter { this.setPage(curPage || this.page?.data?.id); } - setPage(id?: Id) { + public setPage(id?: Id) { let page; if (id) { @@ -165,54 +174,77 @@ class App extends EventEmitter { } } - registerComponent(type: string, Component: any) { + public registerComponent(type: string, Component: any) { this.components.set(type, Component); } - unregisterComponent(type: string) { + public unregisterComponent(type: string) { this.components.delete(type); } - resolveComponent(type: string) { + public resolveComponent(type: string) { return this.components.get(type); } - bindEvents() { + public bindEvents() { if (!this.page) return; this.removeAllListeners(); for (const [, value] of this.page.nodes) { - value.events?.forEach((event) => { - let { name: eventName } = event; - if (DEFAULT_EVENTS.findIndex((defaultEvent) => defaultEvent.value === eventName) > -1) { - // common 事件名通过 node id 避免重复触发 - eventName = getCommonEventName(eventName, `${value.data.id}`); - } + value.events?.forEach((event) => this.bindEvent(event, `${value.data.id}`)); + } + } - this.on(eventName, (fromCpt, ...args) => { - if (!this.page) throw new Error('当前没有页面'); + public bindEvent(event: EventItemConfig, id: string) { + let { name: eventName } = event; + if (DEFAULT_EVENTS.findIndex((defaultEvent) => defaultEvent.value === eventName) > -1) { + // common 事件名通过 node id 避免重复触发 + eventName = getCommonEventName(eventName, id); + } - const toNode = this.page.getNode(event.to); - if (!toNode) throw `ID为${event.to}的组件不存在`; + this.on(eventName, (fromCpt, ...args) => { + this.eventHandler(event, fromCpt, args); + }); + } - const { method: methodName } = event; - if (isCommonMethod(methodName)) { - return triggerCommonMethod(methodName, toNode); - } + public eventHandler(eventConfig: EventItemConfig, fromCpt: any, args: any[]) { + if (!this.page) throw new Error('当前没有页面'); - if (typeof toNode.instance?.[methodName] === 'function') { - toNode.instance[methodName](fromCpt, ...args); - } - }); + const { method: methodName, to } = eventConfig; + + const toNode = this.page.getNode(to); + if (!toNode) throw `ID为${to}的组件不存在`; + + if (isCommonMethod(methodName)) { + return triggerCommonMethod(methodName, toNode); + } + + if (toNode.instance) { + if (typeof toNode.instance[methodName] === 'function') { + toNode.instance[methodName](fromCpt, ...args); + } + } else { + this.addEventToMap({ + eventConfig, + fromCpt, + args, }); } } - destroy() { + public destroy() { this.removeAllListeners(); this.pages.clear(); } + + private addEventToMap(event: EventCache) { + if (this.eventQueueMap[event.eventConfig.to]) { + this.eventQueueMap[event.eventConfig.to].push(event); + } else { + this.eventQueueMap[event.eventConfig.to] = [event]; + } + } } export default App; diff --git a/packages/core/src/Node.ts b/packages/core/src/Node.ts index 29e1700e..61de6f64 100644 --- a/packages/core/src/Node.ts +++ b/packages/core/src/Node.ts @@ -20,19 +20,34 @@ import { EventEmitter } from 'events'; import type { EventItemConfig, MComponent, MContainer, MPage } from '@tmagic/schema'; +import type App from './App'; +import type Page from './Page'; + +interface NodeOptions { + config: MComponent | MContainer; + page?: Page; + parent?: Node; + app: App; +} class Node extends EventEmitter { - data: MComponent | MContainer | MPage; - style?: { + public data: MComponent | MContainer | MPage; + public style?: { [key: string]: any; }; - events?: EventItemConfig[]; - instance?: any; + public events?: EventItemConfig[]; + public instance?: any; + public page?: Page; + public parent?: Node; + public app: App; - constructor(config: MComponent | MContainer) { + constructor(options: NodeOptions) { super(); - const { events } = config; - this.data = config; + this.page = options.page; + this.parent = options.parent; + this.app = options.app; + const { events } = options.config; + this.data = options.config; this.events = events; this.listenLifeSafe(); @@ -47,9 +62,10 @@ class Node extends EventEmitter { }); } - listenLifeSafe() { + private listenLifeSafe() { this.once('created', (instance: any) => { this.instance = instance; + if (typeof this.data.created === 'function') { this.data.created(this); } @@ -57,6 +73,13 @@ class Node extends EventEmitter { this.once('mounted', (instance: any) => { this.instance = instance; + + const eventConfigQueue = this.app.eventQueueMap[instance.config.id] || []; + + for (let eventConfig = eventConfigQueue.shift(); eventConfig; eventConfig = eventConfigQueue.shift()) { + this.app.eventHandler(eventConfig.eventConfig, eventConfig.fromCpt, eventConfig.args); + } + if (typeof this.data.mounted === 'function') { this.data.mounted(this); } diff --git a/packages/core/src/Page.ts b/packages/core/src/Page.ts index 8e3876df..4077ef93 100644 --- a/packages/core/src/Page.ts +++ b/packages/core/src/Page.ts @@ -18,38 +18,47 @@ import type { Id, MComponent, MContainer, MPage } from '@tmagic/schema'; +import type App from './App'; import Node from './Node'; interface ConfigOptions { config: MPage; + app: App; } class Page extends Node { - nodes = new Map(); + public nodes = new Map(); constructor(options: ConfigOptions) { - super(options.config); + super(options); this.setNode(options.config.id, this); - this.initNode(options.config); + this.initNode(options.config, this); } - initNode(config: MComponent | MContainer) { - this.setNode(config.id, new Node(config)); + public initNode(config: MComponent | MContainer, parent: Node) { + const node = new Node({ + config, + parent, + page: this, + app: this.app, + }); + + this.setNode(config.id, node); config.items?.forEach((element: MComponent | MContainer) => { - this.initNode(element); + this.initNode(element, node); }); } - getNode(id: Id) { + public getNode(id: Id) { return this.nodes.get(id); } - setNode(id: Id, node: Node) { + public setNode(id: Id, node: Node) { this.nodes.set(id, node); } - deleteNode(id: Id) { + public deleteNode(id: Id) { this.nodes.delete(id); } }