fix(core): 事件触发时组件未初始化,等组件初始化后再调用事件处理

This commit is contained in:
roymondchen 2022-07-19 15:38:35 +08:00 committed by jia000
parent a4abf5feea
commit 1750467d5b
3 changed files with 114 additions and 50 deletions

View File

@ -18,7 +18,7 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import type { Id, MApp } from '@tmagic/schema'; import type { EventItemConfig, Id, MApp } from '@tmagic/schema';
import Env from './Env'; import Env from './Env';
import { import {
@ -40,17 +40,25 @@ interface AppOptionsConfig {
transformStyle?: (style: Record<string, any>) => Record<string, any>; transformStyle?: (style: Record<string, any>) => Record<string, any>;
} }
interface EventCache {
eventConfig: EventItemConfig;
fromCpt: any;
args: any[];
}
class App extends EventEmitter { class App extends EventEmitter {
env; public env;
pages = new Map<Id, Page>(); public pages = new Map<Id, Page>();
page: Page | undefined; public page: Page | undefined;
platform = 'mobile'; public platform = 'mobile';
jsEngine = 'browser'; public jsEngine = 'browser';
components = new Map(); public components = new Map();
public eventQueueMap: Record<string, EventCache[]> = {};
constructor(options: AppOptionsConfig) { constructor(options: AppOptionsConfig) {
super(); super();
@ -89,7 +97,7 @@ class App extends EventEmitter {
* @param style Object * @param style Object
* @returns Object * @returns Object
*/ */
transformStyle(style: Record<string, any> | string) { public transformStyle(style: Record<string, any> | string) {
if (!style) { if (!style) {
return {}; return {};
} }
@ -132,7 +140,7 @@ class App extends EventEmitter {
* @param config dsl跟节点 * @param config dsl跟节点
* @param curPage id * @param curPage id
*/ */
setConfig(config: MApp, curPage?: Id) { public setConfig(config: MApp, curPage?: Id) {
this.pages = new Map(); this.pages = new Map();
config.items?.forEach((page) => { config.items?.forEach((page) => {
@ -140,6 +148,7 @@ class App extends EventEmitter {
page.id, page.id,
new Page({ new Page({
config: page, config: page,
app: this,
}), }),
); );
}); });
@ -147,7 +156,7 @@ class App extends EventEmitter {
this.setPage(curPage || this.page?.data?.id); this.setPage(curPage || this.page?.data?.id);
} }
setPage(id?: Id) { public setPage(id?: Id) {
let page; let page;
if (id) { 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); this.components.set(type, Component);
} }
unregisterComponent(type: string) { public unregisterComponent(type: string) {
this.components.delete(type); this.components.delete(type);
} }
resolveComponent(type: string) { public resolveComponent(type: string) {
return this.components.get(type); return this.components.get(type);
} }
bindEvents() { public bindEvents() {
if (!this.page) return; if (!this.page) return;
this.removeAllListeners(); this.removeAllListeners();
for (const [, value] of this.page.nodes) { for (const [, value] of this.page.nodes) {
value.events?.forEach((event) => { value.events?.forEach((event) => this.bindEvent(event, `${value.data.id}`));
}
}
public bindEvent(event: EventItemConfig, id: string) {
let { name: eventName } = event; let { name: eventName } = event;
if (DEFAULT_EVENTS.findIndex((defaultEvent) => defaultEvent.value === eventName) > -1) { if (DEFAULT_EVENTS.findIndex((defaultEvent) => defaultEvent.value === eventName) > -1) {
// common 事件名通过 node id 避免重复触发 // common 事件名通过 node id 避免重复触发
eventName = getCommonEventName(eventName, `${value.data.id}`); eventName = getCommonEventName(eventName, id);
} }
this.on(eventName, (fromCpt, ...args) => { this.on(eventName, (fromCpt, ...args) => {
this.eventHandler(event, fromCpt, args);
});
}
public eventHandler(eventConfig: EventItemConfig, fromCpt: any, args: any[]) {
if (!this.page) throw new Error('当前没有页面'); if (!this.page) throw new Error('当前没有页面');
const toNode = this.page.getNode(event.to); const { method: methodName, to } = eventConfig;
if (!toNode) throw `ID为${event.to}的组件不存在`;
const toNode = this.page.getNode(to);
if (!toNode) throw `ID为${to}的组件不存在`;
const { method: methodName } = event;
if (isCommonMethod(methodName)) { if (isCommonMethod(methodName)) {
return triggerCommonMethod(methodName, toNode); return triggerCommonMethod(methodName, toNode);
} }
if (typeof toNode.instance?.[methodName] === 'function') { if (toNode.instance) {
if (typeof toNode.instance[methodName] === 'function') {
toNode.instance[methodName](fromCpt, ...args); toNode.instance[methodName](fromCpt, ...args);
} }
}); } else {
this.addEventToMap({
eventConfig,
fromCpt,
args,
}); });
} }
} }
destroy() { public destroy() {
this.removeAllListeners(); this.removeAllListeners();
this.pages.clear(); 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; export default App;

View File

@ -20,19 +20,34 @@ import { EventEmitter } from 'events';
import type { EventItemConfig, MComponent, MContainer, MPage } from '@tmagic/schema'; 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 { class Node extends EventEmitter {
data: MComponent | MContainer | MPage; public data: MComponent | MContainer | MPage;
style?: { public style?: {
[key: string]: any; [key: string]: any;
}; };
events?: EventItemConfig[]; public events?: EventItemConfig[];
instance?: any; public instance?: any;
public page?: Page;
public parent?: Node;
public app: App;
constructor(config: MComponent | MContainer) { constructor(options: NodeOptions) {
super(); super();
const { events } = config; this.page = options.page;
this.data = config; this.parent = options.parent;
this.app = options.app;
const { events } = options.config;
this.data = options.config;
this.events = events; this.events = events;
this.listenLifeSafe(); this.listenLifeSafe();
@ -47,9 +62,10 @@ class Node extends EventEmitter {
}); });
} }
listenLifeSafe() { private listenLifeSafe() {
this.once('created', (instance: any) => { this.once('created', (instance: any) => {
this.instance = instance; this.instance = instance;
if (typeof this.data.created === 'function') { if (typeof this.data.created === 'function') {
this.data.created(this); this.data.created(this);
} }
@ -57,6 +73,13 @@ class Node extends EventEmitter {
this.once('mounted', (instance: any) => { this.once('mounted', (instance: any) => {
this.instance = instance; 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') { if (typeof this.data.mounted === 'function') {
this.data.mounted(this); this.data.mounted(this);
} }

View File

@ -18,38 +18,47 @@
import type { Id, MComponent, MContainer, MPage } from '@tmagic/schema'; import type { Id, MComponent, MContainer, MPage } from '@tmagic/schema';
import type App from './App';
import Node from './Node'; import Node from './Node';
interface ConfigOptions { interface ConfigOptions {
config: MPage; config: MPage;
app: App;
} }
class Page extends Node { class Page extends Node {
nodes = new Map<Id, Node>(); public nodes = new Map<Id, Node>();
constructor(options: ConfigOptions) { constructor(options: ConfigOptions) {
super(options.config); super(options);
this.setNode(options.config.id, this); this.setNode(options.config.id, this);
this.initNode(options.config); this.initNode(options.config, this);
} }
initNode(config: MComponent | MContainer) { public initNode(config: MComponent | MContainer, parent: Node) {
this.setNode(config.id, new Node(config)); const node = new Node({
config,
parent,
page: this,
app: this.app,
});
this.setNode(config.id, node);
config.items?.forEach((element: MComponent | MContainer) => { 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); return this.nodes.get(id);
} }
setNode(id: Id, node: Node) { public setNode(id: Id, node: Node) {
this.nodes.set(id, node); this.nodes.set(id, node);
} }
deleteNode(id: Id) { public deleteNode(id: Id) {
this.nodes.delete(id); this.nodes.delete(id);
} }
} }