feat: 透出错误边界捕捉不到的错误

This commit is contained in:
wuyue.xht 2020-09-01 19:15:46 +08:00
parent 55948e1d3f
commit f224abffff
5 changed files with 44 additions and 362 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@ali/lowcode-react-provider",
"version": "1.0.6-0",
"version": "1.0.6-1",
"description": "React Provider for Runtime",
"files": [
"es",

View File

@ -32,19 +32,24 @@ export default class LazyComponent extends Component<IProps, IState> {
const schema = await getPageData();
this.setState({ schema });
context.emitPageReady();
} catch (error) {
} catch (err) {
this.setState({ hasError: true });
this.exeAfterCatch(err.message, err.stack);
}
}
}
componentDidCatch(error: any, errorInfo: any) {
exeAfterCatch(error: any, errorInfo?: any) {
const { afterCatch } = app.getErrorBoundary() || {};
if (typeof afterCatch === 'function') {
afterCatch.call(this, error, errorInfo);
}
}
componentDidCatch(error: any, errorInfo: any) {
this.exeAfterCatch(error, errorInfo);
}
render() {
const { schema, hasError } = this.state;
if (hasError) {

View File

@ -1,6 +1,6 @@
{
"name": "@ali/lowcode-runtime",
"version": "1.0.6-0",
"version": "1.0.6-1",
"description": "Runtime for Ali lowCode engine",
"files": [
"es",

View File

@ -1,383 +1,57 @@
import { IAppConfig, IUtils, IComponents, HistoryMode } from './runApp';
import EventEmitter from '@ali/offline-events';
import Container, { ILayoutOptions, IErrorBoundaryConfig } from './container';
import Provider from './provider';
import runApp from './runApp';
interface IConstants {
[key: string]: any;
}
interface IComponentMap {
componentName: string;
package?: string;
version?: string;
destructuring?: boolean;
exportName?: string;
subName?: string;
}
interface ILayoutConfig {
componentName: string;
props: any;
}
interface IRouterConfig {
[key: string]: string;
}
interface IHistoryConfig {
mode: HistoryMode;
basement?: string;
}
export interface IAppData {
history?: HistoryMode;
layout?: ILayoutConfig;
routes?: IRouterConfig;
containerId?: string;
components?: IComponents;
componentsMap?: IComponentMap[];
utils?: IUtils;
constants?: IConstants;
i18n?: I18n;
}
export interface ComponentProps {
[key: string]: any;
}
export interface JSExpression {
type: string;
value: string;
[key: string]: string;
}
export interface DataSourceItem {
id: string;
isInit: boolean;
type: string;
options: {
uri: string;
params: object;
method: string;
shouldFetch?: string;
willFetch?: string;
fit?: string;
didFetch?: string;
};
dataHandler: JSExpression;
}
export interface DataSource {
list: DataSourceItem[];
dataHandler: JSExpression;
}
export interface LifeCycles {
[key: string]: JSExpression;
}
export interface Methods {
[key: string]: JSExpression;
}
export interface ComponentModel {
id?: string;
componentName: string;
fileName?: string;
props?: ComponentProps;
css?: string;
dataSource?: DataSource;
lifeCycles?: LifeCycles;
methods?: Methods;
children?: ComponentModel[] | string[];
condition?: JSExpression | boolean;
loop?: string[];
loopArgs?: string[];
}
export interface I18n {
'zh-CN': { [key: string]: string };
'en-US': { [key: string]: string };
}
type Locale = 'zh-CN' | 'en-US';
export default class Provider {
emitter: EventEmitter = new EventEmitter();
components: IComponents = {};
utils: IUtils = {};
constants: IConstants = {};
routes: IRouterConfig | null = null;
layout: ILayoutConfig | null = null;
componentsMap: IComponentMap[] = [];
history: HistoryMode = 'hash';
containerId = '';
i18n: I18n | null = null;
homePage = '';
lazyElementsMap: { [key: string]: any } = {};
isSectionalRender = false;
class App {
private container: Container;
constructor() {
this.init();
this.container = new Container();
}
async(): Promise<IAppConfig> {
return new Promise(async (resolve, reject) => {
try {
const appData: IAppData = await this.getAppData();
if (!appData) {
return;
}
const { history, layout, routes, containerId, components, componentsMap, utils, constants, i18n } = appData;
this.setHistory(history);
this.setLayoutConfig(layout);
this.setRouterConfig(routes);
this.setContainerId(containerId);
this.setI18n(i18n);
this.registerComponents(components);
this.registerComponentsMap(componentsMap);
this.registerUtils(utils);
this.registerContants(constants);
resolve({
history: this.getHistory(),
components: this.getComponents(),
utils: this.getUtils(),
containerId: this.getContainerId(),
});
} catch (err) {
reject(err);
}
});
run() {
runApp();
}
init() {
// 默认 ready当重载了init时需手动触发 this.ready()
this.ready();
registerRenderer(renderer: any): any {
this.container.registerRenderer(renderer);
}
ready(params?: any) {
if (params && typeof params === 'function') {
params = params();
}
this.emitter.emit('ready', params || '');
registerLayout(Layout: any, options: ILayoutOptions): any {
this.container.registerLayout(Layout, options);
}
onReady(cb: (params?: any) => void) {
if (!cb || typeof cb !== 'function') {
return;
}
this.emitter.on('ready', cb);
registerLoading(component: any) {
this.container.registerLoading(component);
}
emitPageReady() {
this.emitter.emit('pageReady');
registerProvider(CustomProvider: any) {
this.container.registerProvider(CustomProvider);
}
emitPageEnter() {
this.emitter.emit('pageEnter');
registerErrorBoundary(config: IErrorBoundaryConfig) {
this.container.registerErrorBoundary(config);
}
emitPageUpdate() {
this.emitter.emit('pageUpdate');
getLayout(componentName: string) {
return this.container.getLayout(componentName);
}
emitPageLeave() {
this.emitter.emit('pageLeave');
getRenderer(): any {
return this.container.getRenderer();
}
onPageReady(cb: (params?: any) => void) {
this.emitter.on('pageReady', cb);
return () => {
this.emitter.removeListener('pageReady', cb);
};
getLoading(): any {
return this.container.getLoading();
}
onPageEnter(cb: (params?: any) => void) {
this.emitter.on('pageEnter', cb);
return () => {
this.emitter.removeListener('pageEnter', cb);
};
getErrorBoundary(): IErrorBoundaryConfig {
return this.container.getErrorBoundary();
}
onPageUpdate(cb: (params?: any) => void) {
this.emitter.on('pageUpdate', cb);
return () => {
this.emitter.removeListener('pageUpdate', cb);
};
}
onPageLeave(cb: (params?: any) => void) {
this.emitter.on('pageLeave', cb);
return () => {
this.emitter.removeListener('pageLeave', cb);
};
}
getAppData(): any {
throw new Error('Method called "getAppData" not implemented.');
}
getPageData(pageId?: string): any {
throw new Error('Method called "getPageData" not implemented.');
}
getLazyComponent(pageId: string, props: any): any {
throw new Error('Method called "getLazyComponent" not implemented.');
}
// 定制构造根组件的逻辑,如切换路由机制
createApp() {
throw new Error('Method called "createApp" not implemented.');
}
runApp(App: any, config: IAppConfig) {
throw new Error('Method called "runApp" not implemented.');
}
registerComponents(components: IComponents | undefined) {
if (!components) {
return;
}
this.components = components;
}
registerComponentsMap(componentsMap: IComponentMap[] | undefined) {
if (!componentsMap) {
return;
}
this.componentsMap = componentsMap;
}
registerUtils(utils: IUtils | undefined) {
if (!utils) {
return;
}
this.utils = utils;
}
registerContants(constants: IConstants | undefined) {
if (!constants) {
return;
}
this.constants = constants;
}
setLayoutConfig(config: ILayoutConfig | undefined) {
if (!config) {
return;
}
this.layout = config;
}
setRouterConfig(config: IRouterConfig | undefined) {
if (!config) {
return;
}
this.routes = config;
}
setHistory(config: HistoryMode | undefined): any {
if (!config) {
return;
}
this.history = config;
}
setContainerId(id: string | undefined) {
if (!id) {
return;
}
this.containerId = id;
}
setI18n(i18n: I18n | undefined) {
if (!i18n) {
return;
}
this.i18n = i18n;
}
setLazyElement(pageId: string, cache: any) {
if (!pageId || !cache) {
return;
}
this.lazyElementsMap[pageId] = cache;
}
setHomePage(pageId: string) {
if (pageId) {
this.homePage = pageId;
}
}
getComponents() {
return this.components;
}
getComponent(name: string) {
if (!name) {
return;
}
return this.components[name];
}
getUtils() {
return this.utils;
}
getConstants() {
return this.constants;
}
getComponentsMap() {
return this.componentsMap;
}
getComponentsMapObj() {
const compMapArr = this.getComponentsMap();
if (!compMapArr || !Array.isArray(compMapArr)) {
return;
}
const compMapObj: any = {};
compMapArr.forEach((item: IComponentMap) => {
if (!item || !item.componentName) {
return;
}
compMapObj[item.componentName] = item;
});
return compMapObj;
}
getLayoutConfig() {
return this.layout;
}
getRouterConfig() {
return this.routes;
}
getHistory() {
return this.history;
}
getContainerId() {
return this.containerId || 'App';
}
getI18n(locale?: Locale) {
if (!this.i18n) {
return;
}
return locale ? this.i18n[locale] : this.i18n;
}
getHomePage() {
return this.homePage;
}
getLazyElement(pageId: string) {
if (!pageId) {
return;
}
return this.lazyElementsMap[pageId];
getProvider(id?: string): Provider | undefined {
return this.container.getProvider(id);
}
}
export default new App();

View File

@ -36,9 +36,12 @@ export default function runApp() {
}
const App = provider.createApp();
provider.runApp(App, config);
}).catch((err) => {
}).catch((err: Error) => {
console.error(err.message);
const { fallbackUI } = app.getErrorBoundary() || {};
const { fallbackUI, afterCatch } = app.getErrorBoundary() || {};
if (typeof afterCatch === 'function') {
afterCatch(err.message, err.stack);
}
if (!fallbackUI) {
return;
}