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 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<string, any>) => Record<string, any>;
}
interface EventCache {
eventConfig: EventItemConfig;
fromCpt: any;
args: any[];
}
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';
jsEngine = 'browser';
public platform = 'mobile';
public jsEngine = 'browser';
components = new Map();
public components = new Map();
public eventQueueMap: Record<string, EventCache[]> = {};
constructor(options: AppOptionsConfig) {
super();
@ -89,7 +97,7 @@ class App extends EventEmitter {
* @param style Object
* @returns Object
*/
transformStyle(style: Record<string, any> | string) {
public transformStyle(style: Record<string, any> | 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;

View File

@ -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);
}

View File

@ -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<Id, Node>();
public nodes = new Map<Id, Node>();
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);
}
}