From fae12ac8a7f955bc6061d54fba5d13da171f781d Mon Sep 17 00:00:00 2001 From: "wuyue.xht" Date: Tue, 31 Mar 2020 02:02:02 +0800 Subject: [PATCH] refactor: runtime --- packages/demo/public/preview.html | 1 + packages/demo/src/preview.js | 20 +- packages/demo/src/preview/config/app.js | 10 +- packages/demo/src/preview/config/constants.js | 1 + .../src/preview/layouts/BasicLayout/index.js | 4 +- .../BasicLayout/{index.less => index.scss} | 6 +- packages/demo/src/preview/plugins/provider.ts | 107 ++----- packages/runtime/src/boot.ts | 44 --- packages/runtime/src/core/boot.ts | 52 +++ packages/runtime/src/core/provider/base.ts | 297 ++++++++++++++++++ .../runtime/src/core/provider/react/index.ts | 80 +++++ .../provider/react/lazy-component.tsx} | 2 +- packages/runtime/src/core/run.ts | 66 ++++ packages/runtime/src/index.ts | 11 +- packages/runtime/src/provider.ts | 90 ------ .../utils.js => runtime/src/utils/assets.ts} | 61 +++- packages/runtime/src/utils/index.ts | 1 + 17 files changed, 591 insertions(+), 262 deletions(-) create mode 100644 packages/demo/src/preview/config/constants.js rename packages/demo/src/preview/layouts/BasicLayout/{index.less => index.scss} (75%) delete mode 100644 packages/runtime/src/boot.ts create mode 100644 packages/runtime/src/core/boot.ts create mode 100644 packages/runtime/src/core/provider/base.ts create mode 100644 packages/runtime/src/core/provider/react/index.ts rename packages/runtime/src/{lazyComponent.tsx => core/provider/react/lazy-component.tsx} (96%) create mode 100644 packages/runtime/src/core/run.ts delete mode 100644 packages/runtime/src/provider.ts rename packages/{demo/src/preview/plugins/utils.js => runtime/src/utils/assets.ts} (59%) create mode 100644 packages/runtime/src/utils/index.ts diff --git a/packages/demo/public/preview.html b/packages/demo/public/preview.html index 34c8cf86c..c8f2cfdcb 100644 --- a/packages/demo/public/preview.html +++ b/packages/demo/public/preview.html @@ -16,5 +16,6 @@ +
diff --git a/packages/demo/src/preview.js b/packages/demo/src/preview.js index a7d32c15e..cf463c883 100644 --- a/packages/demo/src/preview.js +++ b/packages/demo/src/preview.js @@ -1,22 +1,20 @@ -import { boot, run } from '@ali/lowcode-runtime'; +import { boot as core, run } from '@ali/lowcode-runtime'; import Renderer from '@ali/lowcode-react-renderer'; import FusionLoading from './preview/plugins/loading/fusion'; import BasicLayout from './preview/layouts/BasicLayout'; -import provider from './preview/plugins/provider'; +import Preview from './preview/plugins/provider'; // 注册渲染模块 -boot.registerRenderer(Renderer); +core.registerRenderer(Renderer); // 注册布局组件,可注册多个 -boot.registerLayout('BasicLayout', BasicLayout); +core.registerLayout('BasicLayout', BasicLayout); // 注册页面 Loading -boot.registerLoading(FusionLoading); +core.registerLoading(FusionLoading); -const appProvider = provider.create('lowcode_demo'); // 入参为应用唯一标识 +// appKey:应用唯一标识 +core.registerProvider(new Preview({ appKey: 'lowcode_demo' })); -// 异步加载应用配置 -appProvider.then(({ App, config }) => { - // 启动应用 - run(App, config); -}); +// 启动应用 +run(); diff --git a/packages/demo/src/preview/config/app.js b/packages/demo/src/preview/config/app.js index c1ad5b57c..6562cebbf 100644 --- a/packages/demo/src/preview/config/app.js +++ b/packages/demo/src/preview/config/app.js @@ -1,15 +1,15 @@ export default { sdkVersion: '1.0.3', - historyMode: 'hash', // 浏览器路由:brower 哈希路由:hash - constainerId: 'app', + history: 'hash', // 浏览器路由:brower 哈希路由:hash + containerId: 'lce-container', layout: { componentName: 'BasicLayout', props: { name: '低代码引擎预览 demo', logo: { - src: 'https://img.alicdn.com/tfs/TB1kAfWyrY1gK0jSZTEXXXDQVXa-75-33.png', - width: 40, - height: 20, + src: 'https://img.alicdn.com/tfs/TB1L.1QAeL2gK0jSZFmXXc7iXXa-90-90.png', + width: 25, + height: 25, }, }, }, diff --git a/packages/demo/src/preview/config/constants.js b/packages/demo/src/preview/config/constants.js new file mode 100644 index 000000000..ff8b4c563 --- /dev/null +++ b/packages/demo/src/preview/config/constants.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/demo/src/preview/layouts/BasicLayout/index.js b/packages/demo/src/preview/layouts/BasicLayout/index.js index 6a51fdc75..38a488e17 100644 --- a/packages/demo/src/preview/layouts/BasicLayout/index.js +++ b/packages/demo/src/preview/layouts/BasicLayout/index.js @@ -1,9 +1,9 @@ import { Search, Icon, Shell } from '@alifd/next'; -import './index.less'; +import './index.scss'; // eslint-disable-next-line react/prop-types export default ({ name, children, logo }) => ( - + logo {name} diff --git a/packages/demo/src/preview/layouts/BasicLayout/index.less b/packages/demo/src/preview/layouts/BasicLayout/index.scss similarity index 75% rename from packages/demo/src/preview/layouts/BasicLayout/index.less rename to packages/demo/src/preview/layouts/BasicLayout/index.scss index 000abd1c8..a8fe9848a 100644 --- a/packages/demo/src/preview/layouts/BasicLayout/index.less +++ b/packages/demo/src/preview/layouts/BasicLayout/index.scss @@ -1,4 +1,4 @@ -@header-height: 52px; +$header-height: 52px; .avatar { width: 24px; @@ -10,13 +10,13 @@ .basic-shell { min-height: 100vh; .next-shell-header { - height: @header-height; + height: $header-height; } .next-shell-main { flex: 1; display: flex; flex-flow: column; - min-height: calc(100% - @header-height); + min-height: calc(100% - $header-height); .next-shell-sub-main { flex: 1; } diff --git a/packages/demo/src/preview/plugins/provider.ts b/packages/demo/src/preview/plugins/provider.ts index fc56ece03..4cb0d513a 100644 --- a/packages/demo/src/preview/plugins/provider.ts +++ b/packages/demo/src/preview/plugins/provider.ts @@ -1,45 +1,45 @@ -import { createElement } from 'react'; -import { Provider, boot, Router } from '@ali/lowcode-runtime'; +import { ReactProvider, Utils } from '@ali/lowcode-runtime'; import appConfig from '../config/app'; import builtInComps from '../config/components'; import componentsMap from '../config/componentsMap'; -import util from '../config/utils'; -import { buildComponents } from './utils'; +import constants from '../config/constants'; +import utils from '../config/utils'; // 定制加载应用配置的逻辑 -class PreviewProvider extends Provider { +export default class Preview extends ReactProvider { // 定制获取、处理应用配置(组件、插件、路由模式、布局等)的逻辑 - async getAppData(appkey: string, restOptions?: any): Promise { - const { historyMode, layout, constainerId } = appConfig; + async getAppData(appkey: string): Promise { + const { history, layout, containerId } = appConfig; const appSchemaStr: any = localStorage.getItem('lce-dev-store'); - const appSchema = JSON.parse(appSchemaStr || ''); - const history = { - mode: historyMode || 'hash', - basement: '/', - }; - this.layout = layout; + if (!appSchemaStr) { + return; + } + const appSchema = JSON.parse(appSchemaStr); + if (!appSchema) { + return; + } const routes: any = {}; - appSchema.componentsTree.forEach((page: any, idx: number) => { + appSchema.componentsTree.forEach((page: any) => { if (!page.fileName) { return; } const pageId = page.fileName; routes[pageId] = `/${pageId}`; }); - this.routerConfig = routes; - this.componentsMap = componentsMap; - this.globalComponents = { ...builtInComps, ...buildComponents({ '@alifd/next': 'Next' }, componentsMap) }; - this.globalUtils = util; return { history, - globalComponents: this.globalComponents, - globalUtils: this.globalUtils, - constainerId, + layout, + routes, + containerId, + components: { ...builtInComps, ...Utils.buildComponents({ '@alifd/next': 'Next' }, componentsMap) }, + componentsMap, + utils: utils, + constants, }; } // 定制获取、处理页面 schema 的逻辑 - async getPageData(pageId: string, restOptions?: any) { + async getPageData(pageId: string) { const appSchemaStr = localStorage.getItem('lce-dev-store'); const appSchema = JSON.parse(appSchemaStr || ''); const idx = appSchema.componentsTree.findIndex( @@ -48,67 +48,4 @@ class PreviewProvider extends Provider { const schema = appSchema.componentsTree[idx]; return schema; } - - // 定制构造根组件的逻辑,如切换路由机制 - createApp() { - if (!this.routerConfig) { - return; - } - const routes: Array<{ path: string; children: any; exact: boolean; keepAlive: boolean }> = []; - let homePageId = ''; - Object.keys(this.routerConfig).forEach((pageId: string, idx: number) => { - if (!pageId) { - return; - } - const path = this.routerConfig[pageId]; - if (idx === 0 || path === '/') { - homePageId = pageId; - } - routes.push({ - path, - children: (props: any) => this.getLazyComponent(pageId, props), - exact: true, - keepAlive: true, - }); - }); - if (homePageId) { - routes.push({ - path: '**', - children: (props: any) => this.getLazyComponent(homePageId, { ...props }), - exact: true, - keepAlive: true, - }); - } - const RouterView = (props: any) => { - return createElement(Router as any, { - routes, - components: this.globalComponents, - utils: this.globalUtils, - componentsMap: this.componentsMap, - ...props, - }); - }; - let App; - if (!this.layout || !(this.layout as any).componentName) { - App = (props: any) => createElement(RouterView, { ...props }); - return App; - } - const { componentName: layoutName, props: layoutProps } = this.layout as any; - const Layout = boot.getLayout(layoutName); - if (Layout) { - App = (props: any) => - createElement( - Layout, - { - ...layoutProps, - }, - RouterView({ props }), - ); - } else { - App = (props: any) => createElement(RouterView, props); - } - return App; - } } - -export default new PreviewProvider(); diff --git a/packages/runtime/src/boot.ts b/packages/runtime/src/boot.ts deleted file mode 100644 index 6b99987e1..000000000 --- a/packages/runtime/src/boot.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ComponentClass, FunctionComponent } from 'react'; - -type TComponent = ComponentClass | FunctionComponent; - -class Trunk { - private renderer: TComponent | null = null; - private layouts: { [key: string]: TComponent } = {}; - private loading: TComponent | null = null; - - registerRenderer(renderer: TComponent): any { - this.renderer = renderer; - } - - registerLayout(componentName: string, Layout: TComponent): any { - if (!componentName || !Layout) { - return; - } - this.layouts[componentName] = Layout; - } - - registerLoading(component: TComponent) { - if (!component) { - return; - } - this.loading = component; - } - - getLayout(componentName: string) { - if (!componentName) { - return; - } - return this.layouts[componentName]; - } - - getRenderer(): TComponent | null { - return this.renderer; - } - - getLoading(): TComponent | null { - return this.loading; - } -} - -export default new Trunk(); diff --git a/packages/runtime/src/core/boot.ts b/packages/runtime/src/core/boot.ts new file mode 100644 index 000000000..9c96637dd --- /dev/null +++ b/packages/runtime/src/core/boot.ts @@ -0,0 +1,52 @@ +import { ReactType } from 'react'; +import { IProvider } from './provider/base'; + +class Boot { + private renderer: ReactType | null = null; + private layouts: { [key: string]: ReactType } = {}; + private loading: ReactType | null = null; + private provider: any; + + registerRenderer(renderer: ReactType): any { + this.renderer = renderer; + } + + registerLayout(componentName: string, Layout: ReactType): any { + if (!componentName || !Layout) { + return; + } + this.layouts[componentName] = Layout; + } + + registerLoading(component: ReactType) { + if (!component) { + return; + } + this.loading = component; + } + + registerProvider(provider: IProvider) { + this.provider = provider; + } + + getLayout(componentName: string) { + if (!componentName) { + return; + } + return this.layouts[componentName]; + } + + getRenderer(): ReactType | null { + return this.renderer; + } + + getLoading(): ReactType | null { + return this.loading; + } + + getProvider() { + return this.provider; + } +} + +export default new Boot(); diff --git a/packages/runtime/src/core/provider/base.ts b/packages/runtime/src/core/provider/base.ts new file mode 100644 index 000000000..cb792c296 --- /dev/null +++ b/packages/runtime/src/core/provider/base.ts @@ -0,0 +1,297 @@ +import { IAppConfig, IUtils, IComponents, HistoryMode } from '../run'; + +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; +} + +interface IAppData { + history?: HistoryMode; + layout?: ILayoutConfig; + routes?: IRouterConfig; + containerId?: string; + components?: IComponents; + componentsMap?: IComponentMap[]; + utils?: IUtils; + constants?: IConstants; +} + +interface IOptions { + appKey: string; +} + +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[]; + condition?: JSExpression | boolean; + loop?: string[]; + loopArgs?: string[]; +} + +export interface IProvider { + init?(): void; + getAppData?(appkey: string): Promise; + getPageData?(pageId: string): Promise; + getLazyComponent?(pageId: string, props: any): any; + createApp?(): void; +} + +export default class Provider implements IProvider { + private appKey = ''; + private components: IComponents = {}; + private utils: IUtils = {}; + private constants: IConstants = {}; + private routes: IRouterConfig | null = null; + private layout: ILayoutConfig | null = null; + private componentsMap: IComponentMap[] = []; + private history: HistoryMode = 'hash'; + private containerId = ''; + private lazyElementsMap: { [key: string]: any } = {}; + + constructor(options: IOptions) { + const { appKey } = options; + this.appKey = appKey; + this.init(); + } + + async(): Promise { + return new Promise(async (resolve, reject) => { + try { + const appData = await this.getAppData(this.appKey || ''); + if (!appData) { + return; + } + const { history, layout, routes, containerId, components, componentsMap, utils, constants } = appData; + this.setHistory(history); + this.setLayoutConfig(layout); + this.setRouterConfig(routes); + this.setContainerId(containerId); + this.registerComponents(components); + this.registerComponentsMap(componentsMap); + this.registerUtils(utils); + this.registerContants(constants); + resolve({ + history, + components, + utils, + containerId, + }); + } catch (err) { + reject(err.message); + } + }); + } + + async init() { + console.log('init'); + } + + async getAppData(appkey: string): Promise { + throw new Error('Method called "getPageData" not implemented.'); + } + + async getPageData(pageId: string): Promise { + 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.'); + } + + 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) { + if (!config) { + return; + } + this.history = config; + } + + setContainerId(id: string | undefined) { + if (!id) { + return; + } + this.containerId = id; + } + + setlazyElement(pageId: string, cache: any) { + if (!pageId || !cache) { + return; + } + this.lazyElementsMap[pageId] = cache; + } + + 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; + } + + getlazyElement(pageId: string) { + if (!pageId) { + return; + } + return this.lazyElementsMap[pageId]; + } +} diff --git a/packages/runtime/src/core/provider/react/index.ts b/packages/runtime/src/core/provider/react/index.ts new file mode 100644 index 000000000..171af33d7 --- /dev/null +++ b/packages/runtime/src/core/provider/react/index.ts @@ -0,0 +1,80 @@ +import { createElement, ReactElement } from 'react'; +import { Router } from '@ali/recore'; +import boot from '../../boot'; +import Provider from '../base'; +import LazyComponent from './lazy-component'; + +export default class ReactProvider extends Provider { + // 定制构造根组件的逻辑,如切换路由机制 + createApp() { + const routerConfig = this.getRouterConfig(); + if (!routerConfig) { + return; + } + const routes: Array<{ path: string; children: any; exact: boolean; keepAlive: boolean }> = []; + let homePageId = ''; + Object.keys(routerConfig).forEach((pageId: string, idx: number) => { + if (!pageId) { + return; + } + const path = routerConfig[pageId]; + if (idx === 0 || path === '/') { + homePageId = pageId; + } + routes.push({ + path, + children: (props: any) => this.getLazyComponent(pageId, props), + exact: true, + keepAlive: true, + }); + }); + if (homePageId) { + routes.push({ + path: '**', + children: (props: any) => this.getLazyComponent(homePageId, { ...props }), + exact: true, + keepAlive: true, + }); + } + const RouterView = (props: any) => { + return createElement(Router as any, { + routes, + components: this.getComponents(), + utils: this.getUtils(), + componentsMap: this.getComponentsMapObj(), + ...props, + }); + }; + let App; + const layoutConfig = this.getLayoutConfig(); + if (!layoutConfig || !layoutConfig.componentName) { + App = (props: any) => createElement(RouterView, { ...props }); + return App; + } + const { componentName: layoutName, props: layoutProps } = layoutConfig; + const Layout = boot.getLayout(layoutName); + if (Layout) { + App = (props: any) => createElement(Layout, layoutProps, RouterView({ props })); + } else { + App = (props: any) => createElement(RouterView, props); + } + return App; + } + + getLazyComponent(pageId: string, props: any): ReactElement | null { + if (!pageId) { + return null; + } + if (this.getlazyElement(pageId)) { + return this.getlazyElement(pageId); + } else { + const lazyElement = createElement(LazyComponent as any, { + getPageData: async () => await this.getPageData(pageId), + key: pageId, + ...props, + }); + this.setlazyElement(pageId, lazyElement); + return lazyElement; + } + } +} diff --git a/packages/runtime/src/lazyComponent.tsx b/packages/runtime/src/core/provider/react/lazy-component.tsx similarity index 96% rename from packages/runtime/src/lazyComponent.tsx rename to packages/runtime/src/core/provider/react/lazy-component.tsx index 18a582241..7a958202e 100644 --- a/packages/runtime/src/lazyComponent.tsx +++ b/packages/runtime/src/core/provider/react/lazy-component.tsx @@ -1,5 +1,5 @@ import { Component, createElement } from 'react'; -import boot from './boot'; +import boot from '../../boot'; interface IProps { getPageData: () => any; diff --git a/packages/runtime/src/core/run.ts b/packages/runtime/src/core/run.ts new file mode 100644 index 000000000..7cd1a919f --- /dev/null +++ b/packages/runtime/src/core/run.ts @@ -0,0 +1,66 @@ +import { ReactType } from 'react'; +import { runApp } from '@ali/recore'; +import { HashHistoryBuildOptions, BrowserHistoryBuildOptions, MemoryHistoryBuildOptions } from '@recore/history'; +import boot from './boot'; + +export type HistoryOptions = { + mode?: HistoryMode; +} & (HashHistoryBuildOptions | BrowserHistoryBuildOptions | MemoryHistoryBuildOptions); + +export interface IComponents { + [key: string]: ReactType; +} + +export interface IUtils { + [key: string]: any; +} + +export type HistoryMode = 'browser' | 'hash'; + +export interface IAppConfig { + history?: HistoryMode; + components?: IComponents; + utils?: IUtils; + containerId?: string; +} + +export interface IRecoreAppConfig { + history?: HistoryMode; + globalComponents?: IComponents; + globalUtils?: IUtils; + containerId?: string; +} + +function transformConfig(config: IAppConfig | (() => IAppConfig)): IRecoreAppConfig { + if (!config) { + return {}; + } + if (typeof config === 'function') { + config = config(); + } + return { + history: config.history, + globalComponents: config.components, + globalUtils: config.utils, + containerId: config.containerId, + }; +} + +export default function run(config?: IAppConfig | (() => IAppConfig)) { + const provider = boot.getProvider(); + if (config) { + config = transformConfig(config); + const App = provider.createApp(); + runApp(App, config); + return; + } + const promise = provider.async(); + promise.then((config: IAppConfig) => { + if (!config) { + return; + } + const App = provider.createApp(); + config = transformConfig(config); + runApp(App, config); + }); +} diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index dc637c4be..8f92462ab 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1,5 +1,8 @@ -import { navigator, Router, runApp as run } from '@ali/recore'; -import boot from './boot'; -import Provider from './provider'; +import { navigator, Router } from '@ali/recore'; +import run from './core/run'; +import boot from './core/boot'; +import Provider from './core/provider/base'; +import ReactProvider from './core/provider/react'; +import * as Utils from './utils'; -export { run, Router, boot, Provider, navigator }; +export { run, Router, boot, Provider, ReactProvider, navigator, Utils }; diff --git a/packages/runtime/src/provider.ts b/packages/runtime/src/provider.ts deleted file mode 100644 index 9949e10c3..000000000 --- a/packages/runtime/src/provider.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { createElement, ReactElement, ReactType } from 'react'; -import LazyComponent from './lazyComponent'; - -export interface IAppData { - App: any; - config: object; -} - -interface IComponentsMap { - [key: string]: ReactType; -} - -interface IUtilsMap { - [key: string]: any; -} - -type HistoryMode = 'browser' | 'hash'; - -export interface IAppConfig { - history?: HistoryMode; - globalComponents?: IComponentsMap; - globalUtils?: IUtilsMap; - containerId?: string; -} - -export default class Provider { - globalComponents: any = {}; - globalUtils: any = {}; - routerConfig: { [key: string]: string } = {}; - layout: { componentName: string; props: any } | null = null; - componentsMap: any = null; - private lazyElementsMap: { [key: string]: any } = {}; - - constructor() { - this.init(); - } - - create(appkey: string): Promise { - return new Promise(async (resolve, reject) => { - try { - const config = await this.getAppData(appkey); - const App = this.createApp(); - resolve({ - App, - config, - }); - } catch (err) { - reject(err.message); - } - }); - } - - async init() { - console.log('init'); - } - - async getAppData(appkey: string, restOptions?: any): Promise { - console.log('getAppData'); - return {}; - } - - async getPageData(pageId: string, restOptions?: any): Promise { - console.log('getPageData'); - return; - } - - getLazyComponent(pageId: string, props: any): ReactElement | null { - if (!pageId) { - return null; - } - if (this.lazyElementsMap[pageId]) { - console.log('缓存'); - return this.lazyElementsMap[pageId]; - } else { - const lazyElement = createElement(LazyComponent as any, { - getPageData: async () => await this.getPageData(pageId), - key: pageId, - ...props, - }); - this.lazyElementsMap[pageId] = lazyElement; - console.log('新组件'); - return lazyElement; - } - } - - createApp() { - console.log('createApp'); - return; - } -} diff --git a/packages/demo/src/preview/plugins/utils.js b/packages/runtime/src/utils/assets.ts similarity index 59% rename from packages/demo/src/preview/plugins/utils.js rename to packages/runtime/src/utils/assets.ts index b65d4864e..08715003b 100644 --- a/packages/demo/src/preview/plugins/utils.js +++ b/packages/runtime/src/utils/assets.ts @@ -1,17 +1,25 @@ -function isESModule(obj) { +function isESModule(obj: any): obj is { [key: string]: any } { return obj && obj.__esModule; } -function getSubComponent(library, paths) { +function accessLibrary(library: string | object) { + if (typeof library !== 'string') { + return library; + } + + return (window as any)[library]; +} + +function getSubComponent(library: any, paths: string[]) { const l = paths.length; if (l < 1 || !library) { return library; } let i = 0; - let component; + let component: any; while (i < l) { - const key = paths[i]; - let ex; + const key = paths[i]!; + let ex: any; try { component = library[key]; } catch (e) { @@ -32,15 +40,7 @@ function getSubComponent(library, paths) { return component; } -function accessLibrary(library) { - if (typeof library !== 'string') { - return library; - } - - return window[library]; -} - -function findComponent(libraryMap, componentName, npm) { +function findComponent(libraryMap: LibraryMap, componentName: string, npm?: NpmInfo) { if (!npm) { return accessLibrary(componentName); } @@ -62,10 +62,37 @@ function findComponent(libraryMap, componentName, npm) { return getSubComponent(library, paths); } -export function buildComponents(libraryMap, componentsMap) { - const components = {}; +export interface LibraryMap { + [key: string]: string; +} + +export interface NpmInfo { + componentName?: string; + package: string; + version: string; + destructuring?: boolean; + exportName?: string; + subName?: string; + main?: string; +} + +export function buildComponents( + libraryMap: LibraryMap, + componentsMap: { [componentName: string]: NpmInfo } | NpmInfo[], +) { + const components: any = {}; + if (componentsMap && Array.isArray(componentsMap)) { + const compMapObj: any = {}; + componentsMap.forEach((item: NpmInfo) => { + if (!item || !item.componentName) { + return; + } + compMapObj[item.componentName] = item; + }); + componentsMap = compMapObj; + } Object.keys(componentsMap).forEach((componentName) => { - const component = findComponent(libraryMap, componentName, componentsMap[componentName]); + const component = findComponent(libraryMap, componentName, (componentsMap as any)[componentName]); if (component) { components[componentName] = component; } diff --git a/packages/runtime/src/utils/index.ts b/packages/runtime/src/utils/index.ts new file mode 100644 index 000000000..df6446646 --- /dev/null +++ b/packages/runtime/src/utils/index.ts @@ -0,0 +1 @@ +export * from './assets';