diff --git a/packages/react-provider/CHANGELOG.md b/packages/react-provider/CHANGELOG.md new file mode 100644 index 000000000..e4d87c4d4 --- /dev/null +++ b/packages/react-provider/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/packages/react-provider/README.md b/packages/react-provider/README.md new file mode 100644 index 000000000..f37b9b0f6 --- /dev/null +++ b/packages/react-provider/README.md @@ -0,0 +1 @@ +# 低代码引擎运行时框架 diff --git a/packages/react-provider/build.json b/packages/react-provider/build.json new file mode 100644 index 000000000..bd5cf18dd --- /dev/null +++ b/packages/react-provider/build.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "build-plugin-component" + ] +} diff --git a/packages/react-provider/package.json b/packages/react-provider/package.json new file mode 100644 index 000000000..29cca8ab3 --- /dev/null +++ b/packages/react-provider/package.json @@ -0,0 +1,43 @@ +{ + "name": "@ali/lowcode-react-provider", + "version": "0.8.14", + "description": "React Provider for Runtime", + "files": [ + "es", + "lib" + ], + "main": "lib/index.js", + "module": "es/index.js", + "scripts": { + "build": "build-scripts build --skip-demo", + "test": "ava", + "test:snapshot": "ava --update-snapshots" + }, + "ava": { + "compileEnhancements": false, + "snapshotDir": "test/fixtures/__snapshots__", + "extensions": [ + "ts" + ], + "require": [ + "ts-node/register" + ] + }, + "license": "MIT", + "dependencies": { + "@ali/lowcode-runtime": "^0.8.13", + "react": "^16", + "react-dom": "^16", + "@recore/router": "^1.0.11" + }, + "devDependencies": { + "@alib/build-scripts": "^0.1.18", + "build-plugin-component": "^0.2.16", + "@types/node": "^13.7.1", + "@types/react": "^16", + "@types/react-dom": "^16" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + } +} diff --git a/packages/react-provider/src/index.ts b/packages/react-provider/src/index.ts new file mode 100644 index 000000000..515d26e3e --- /dev/null +++ b/packages/react-provider/src/index.ts @@ -0,0 +1,5 @@ +import ReactProvider from './provider'; +import { Router } from '@recore/router'; + +export { Router }; +export default ReactProvider; diff --git a/packages/react-provider/src/lazy-component.tsx b/packages/react-provider/src/lazy-component.tsx new file mode 100644 index 000000000..b20fd2bf8 --- /dev/null +++ b/packages/react-provider/src/lazy-component.tsx @@ -0,0 +1,43 @@ +import { Component, createElement } from 'react'; +import { app } from '@ali/lowcode-runtime'; + +interface IProps { + getPageData: () => any; + [key: string]: any; +} + +interface IState { + schema: object | null; +} + +export default class LazyComponent extends Component { + constructor(props: IProps) { + super(props); + this.state = { + schema: null, + }; + } + + async componentDidMount() { + const { getPageData } = this.props; + if (getPageData && !this.state.schema) { + const schema = await getPageData(); + this.setState({ schema }); + } + } + + render() { + const { getPageData, ...restProps } = this.props; + const { schema } = this.state; + const Renderer = app.getRenderer(); + const Loading = app.getLoading(); + if (!Renderer || !schema) { + if (!Loading) { + return null; + } + // loading扩展点 + return createElement(Loading); + } + return createElement(Renderer as any, { schema, loading: Loading ? createElement(Loading) : null, ...restProps }); + } +} diff --git a/packages/react-provider/src/provider.tsx b/packages/react-provider/src/provider.tsx new file mode 100644 index 000000000..de4d97352 --- /dev/null +++ b/packages/react-provider/src/provider.tsx @@ -0,0 +1,104 @@ +import { createElement, ReactType, ReactElement } from 'react'; +import ReactDOM from 'react-dom'; +import { Router } from '@recore/router'; +import { app, Provider } from '@ali/lowcode-runtime'; +import LazyComponent from './lazy-component'; + +export default class ReactProvider extends Provider { + // 定制构造根组件的逻辑,如切换路由机制 + createApp() { + const RouterView = this.getRouterView(); + let App; + const layoutConfig = this.getLayoutConfig(); + if (!layoutConfig || !layoutConfig.componentName) { + App = (props: any) => (RouterView ? createElement(RouterView, { ...props }) : null); + return App; + } + const { componentName: layoutName, props: layoutProps } = layoutConfig; + const { content: Layout, props: extraLayoutProps } = app.getLayout(layoutName) || {}; + const sectionalRender = this.isSectionalRender(); + if (!sectionalRender && Layout) { + App = (props: any) => + createElement( + Layout, + { ...layoutProps, ...extraLayoutProps }, + RouterView ? createElement(RouterView, props) : null, + ); + } else { + App = (props: any) => (RouterView ? createElement(RouterView, props) : null); + } + return App; + } + + runApp(App: any, config: any) { + ReactDOM.render(, document.getElementById(config?.containerId || 'App')); + } + + // 内置实现 for 动态化渲染 + getRouterView(): ReactType | null { + const routerConfig = this.getRouterConfig(); + if (!routerConfig) { + return null; + } + const routes: Array<{ + path: string; + children: any; + exact: boolean; + defined: { keepAlive: boolean; [key: string]: any }; + }> = []; + let homePageId = this.getHomePage(); + Object.keys(routerConfig).forEach((pageId: string, idx: number) => { + if (!pageId) { + return; + } + const path = routerConfig[pageId]; + routes.push({ + path, + children: (props: any) => this.getLazyComponent(pageId, props), + exact: true, + defined: { keepAlive: true }, + }); + if (homePageId) { + return; + } + if (idx === 0 || path === '/') { + homePageId = pageId; + } + }); + if (homePageId) { + routes.push({ + path: '**', + children: (props: any) => this.getLazyComponent(homePageId, { ...props }), + exact: true, + defined: { keepAlive: true }, + }); + } + const RouterView = (props: any) => { + return createElement(Router as any, { + routes, + components: this.getComponents(), + utils: this.getUtils(), + componentsMap: this.getComponentsMapObj(), + ...props, + }); + }; + return RouterView; + } + + 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/react-provider/tsconfig.json b/packages/react-provider/tsconfig.json new file mode 100644 index 000000000..c37b76ecc --- /dev/null +++ b/packages/react-provider/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "./src/" + ] +}