diff --git a/docs/api/README.md b/docs/api/README.md
index e7182ff55..999293988 100644
--- a/docs/api/README.md
+++ b/docs/api/README.md
@@ -61,6 +61,8 @@ By linking the micro-application to some url rules, the function of automaticall
This function is called when the browser url changes, and `activeRule` returns `true` to indicate that the subapplication needs to be activated.
+ - loader - `(loading: boolean) => void` - optional, function will be invoked while the loading state changed.
+
- props - `object` - optional, data that the primary application needs to pass to the child application.
- `LifeCycles`
diff --git a/docs/zh/api/README.md b/docs/zh/api/README.md
index c51450893..4de3b7819 100644
--- a/docs/zh/api/README.md
+++ b/docs/zh/api/README.md
@@ -61,6 +61,8 @@
浏览器 url 发生变化会调用 activeRule 里的规则,`activeRule` 任意一个返回 `true` 时表明该微应用需要被激活。
+ - loader - `(loading: boolean) => void` - 可选,loading 状态发生变化时会调用的方法。
+
- props - `object` - 可选,主应用需要传递给微应用的数据。
- `LifeCycles`
diff --git a/examples/main/index.js b/examples/main/index.js
index 8567e101a..5336404e1 100644
--- a/examples/main/index.js
+++ b/examples/main/index.js
@@ -16,6 +16,8 @@ import render from './render/ReactRender';
*/
render({ loading: true });
+const loader = loading => render({ loading });
+
/**
* Step2 注册子应用
*/
@@ -26,30 +28,35 @@ registerMicroApps(
name: 'react16',
entry: '//localhost:7100',
container: '#subapp-viewport',
+ loader,
activeRule: '/react16',
},
{
name: 'react15',
entry: '//localhost:7102',
- render,
+ container: '#subapp-viewport',
+ loader,
activeRule: '/react15',
},
{
name: 'vue',
entry: '//localhost:7101',
container: '#subapp-viewport',
+ loader,
activeRule: '/vue',
},
{
name: 'angular9',
entry: '//localhost:7103',
container: '#subapp-viewport',
+ loader,
activeRule: '/angular9',
},
{
name: 'purehtml',
entry: '//localhost:7104',
container: '#subapp-viewport',
+ loader,
activeRule: '/purehtml',
},
],
diff --git a/examples/main/render/ReactRender.jsx b/examples/main/render/ReactRender.jsx
index 66d8a91bf..0e62b1934 100644
--- a/examples/main/render/ReactRender.jsx
+++ b/examples/main/render/ReactRender.jsx
@@ -5,23 +5,17 @@ import ReactDOM from 'react-dom';
* 渲染子应用
*/
function Render(props) {
- const { appContent, loading } = props;
+ const { loading } = props;
return (
<>
- {loading && (
-
Loading...
- )}
-
-
+ {loading && Loading...
}
+
>
);
}
-export default function render({ appContent, loading }) {
+export default function render({ loading }) {
const container = document.getElementById('subapp-container');
- ReactDOM.render(
- ,
- container,
- );
+ ReactDOM.render(, container);
}
diff --git a/src/apis.ts b/src/apis.ts
index 8b57a4263..9b0aac1d3 100644
--- a/src/apis.ts
+++ b/src/apis.ts
@@ -1,8 +1,9 @@
+import { noop } from 'lodash';
import { mountRootParcel, registerApplication, start as startSingleSpa } from 'single-spa';
import { FrameworkConfiguration, FrameworkLifeCycles, LoadableApp, MicroApp, RegistrableApp } from './interfaces';
import { loadApp } from './loader';
import { doPrefetchStrategy } from './prefetch';
-import { Deferred } from './utils';
+import { Deferred, toArray } from './utils';
window.__POWERED_BY_QIANKUN__ = true;
@@ -22,13 +23,24 @@ export function registerMicroApps(
microApps = [...microApps, ...unregisteredApps];
unregisteredApps.forEach(app => {
- const { name, activeRule, props, ...appConfig } = app;
+ const { name, activeRule, loader = noop, props, ...appConfig } = app;
registerApplication({
name,
app: async () => {
+ loader(true);
await frameworkStartedDefer.promise;
- return loadApp({ name, props, ...appConfig }, frameworkConfiguration, lifeCycles);
+
+ const { mount, ...otherMicroAppConfigs } = await loadApp(
+ { name, props, ...appConfig },
+ frameworkConfiguration,
+ lifeCycles,
+ );
+
+ return {
+ mount: [async () => loader(true), ...toArray(mount), async () => loader(false)],
+ ...otherMicroAppConfigs,
+ };
},
activeWhen: activeRule,
customProps: props,
diff --git a/src/interfaces.ts b/src/interfaces.ts
index 637a8965e..72d80d8f6 100644
--- a/src/interfaces.ts
+++ b/src/interfaces.ts
@@ -43,6 +43,7 @@ export type LoadableApp = AppMetadata & { /* props pass t
// for the route-based apps
export type RegistrableApp = LoadableApp & {
+ loader?: (loading: boolean) => void;
activeRule: RegisterApplicationConfig['activeWhen'];
};
diff --git a/src/loader.ts b/src/loader.ts
index 2b7f594bd..480851bd7 100644
--- a/src/loader.ts
+++ b/src/loader.ts
@@ -10,7 +10,7 @@ import getAddOns from './addons';
import { getMicroAppStateActions } from './globalState';
import { FrameworkConfiguration, FrameworkLifeCycles, HTMLContentRender, LifeCycleFn, LoadableApp } from './interfaces';
import { createSandbox } from './sandbox';
-import { Deferred, getDefaultTplWrapper, getWrapperId, validateExportLifecycle } from './utils';
+import { Deferred, getDefaultTplWrapper, getWrapperId, toArray, validateExportLifecycle } from './utils';
function assertElementExist(element: Element | null | undefined, msg?: string) {
if (!element) {
@@ -22,10 +22,6 @@ function assertElementExist(element: Element | null | undefined, msg?: string) {
}
}
-function toArray(array: T | T[]): T[] {
- return Array.isArray(array) ? array : [array];
-}
-
function execHooksChain(hooks: Array>, app: LoadableApp): Promise {
if (hooks.length) {
return hooks.reduce((chain, hook) => chain.then(() => hook(app)), Promise.resolve());
diff --git a/src/sandbox/patchers/dynamicHeadAppend.ts b/src/sandbox/patchers/dynamicHeadAppend.ts
index cb810bd5f..8a30f1ec7 100644
--- a/src/sandbox/patchers/dynamicHeadAppend.ts
+++ b/src/sandbox/patchers/dynamicHeadAppend.ts
@@ -161,7 +161,7 @@ function getNewRemoveChild(...args: any[]) {
}
try {
- // container may had been removed while app unmounting
+ // container may had been removed while app unmounting if the removeChild action was async
const container = appWrapperGetter();
if (container.contains(child)) {
return rawRemoveChild.call(container, child) as T;
diff --git a/src/utils.ts b/src/utils.ts
index 4d9c215c3..10ec6ce06 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -5,6 +5,10 @@
import { isFunction, snakeCase } from 'lodash';
+export function toArray(array: T | T[]): T[] {
+ return Array.isArray(array) ? array : [array];
+}
+
export function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}