@micro-zoe/micro-app 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -324,15 +324,19 @@ declare module '@micro-zoe/micro-app/create_app' {
324
324
  /**
325
325
  * unmount app
326
326
  * @param destroy completely destroy, delete cache resources
327
+ * @param unmountcb callback of unmount
327
328
  */
328
- unmount(destroy: boolean): void;
329
+ unmount(destroy: boolean, unmountcb?: CallableFunction): void;
329
330
  actionsForCompletelyDestory(): void;
331
+ hiddenKeepAliveApp(): void;
332
+ showKeepAliveApp(container: HTMLElement | ShadowRoot): void;
330
333
  /**
331
334
  * app rendering error
332
335
  * @param e Error
333
336
  */
334
337
  onerror(e: Error): void;
335
- getAppStatus(): string;
338
+ getAppState(): string;
339
+ getKeepAliveState(): string | null;
336
340
  }
337
341
  export function getActiveApps(): string[];
338
342
  export function getAllApps(): string[];
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '0.5.3';
1
+ const version = '0.6.0';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -297,16 +297,16 @@ var ObservedAttrName;
297
297
  ObservedAttrName["URL"] = "url";
298
298
  })(ObservedAttrName || (ObservedAttrName = {}));
299
299
  // app status
300
- var appStatus;
301
- (function (appStatus) {
302
- appStatus["NOT_LOADED"] = "NOT_LOADED";
303
- appStatus["LOADING_SOURCE_CODE"] = "LOADING_SOURCE_CODE";
304
- appStatus["LOAD_SOURCE_FINISHED"] = "LOAD_SOURCE_FINISHED";
305
- appStatus["LOAD_SOURCE_ERROR"] = "LOAD_SOURCE_ERROR";
306
- appStatus["MOUNTING"] = "MOUNTING";
307
- appStatus["MOUNTED"] = "MOUNTED";
308
- appStatus["UNMOUNT"] = "UNMOUNT";
309
- })(appStatus || (appStatus = {}));
300
+ var appStates;
301
+ (function (appStates) {
302
+ appStates["NOT_LOADED"] = "NOT_LOADED";
303
+ appStates["LOADING_SOURCE_CODE"] = "LOADING_SOURCE_CODE";
304
+ appStates["LOAD_SOURCE_FINISHED"] = "LOAD_SOURCE_FINISHED";
305
+ appStates["LOAD_SOURCE_ERROR"] = "LOAD_SOURCE_ERROR";
306
+ appStates["MOUNTING"] = "MOUNTING";
307
+ appStates["MOUNTED"] = "MOUNTED";
308
+ appStates["UNMOUNT"] = "UNMOUNT";
309
+ })(appStates || (appStates = {}));
310
310
  // lifecycles
311
311
  var lifeCycles;
312
312
  (function (lifeCycles) {
@@ -315,7 +315,17 @@ var lifeCycles;
315
315
  lifeCycles["MOUNTED"] = "mounted";
316
316
  lifeCycles["UNMOUNT"] = "unmount";
317
317
  lifeCycles["ERROR"] = "error";
318
+ // 👇 keep-alive only
319
+ lifeCycles["BEFORESHOW"] = "beforeshow";
320
+ lifeCycles["AFTERSHOW"] = "aftershow";
321
+ lifeCycles["AFTERHIDDEN"] = "afterhidden";
318
322
  })(lifeCycles || (lifeCycles = {}));
323
+ // keep-alive status
324
+ var keepAliveStates;
325
+ (function (keepAliveStates) {
326
+ keepAliveStates["KEEP_ALIVE_SHOW"] = "KEEP_ALIVE_SHOW";
327
+ keepAliveStates["KEEP_ALIVE_HIDDEN"] = "KEEP_ALIVE_HIDDEN";
328
+ })(keepAliveStates || (keepAliveStates = {}));
319
329
 
320
330
  /**
321
331
  * fetch source of html, js, css
@@ -1317,14 +1327,16 @@ function releaseEffectDocumentEvent() {
1317
1327
  document.addEventListener = globalEnv.rawDocumentAddEventListener;
1318
1328
  document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
1319
1329
  }
1330
+ // this events should be sent to the specified app
1331
+ const formatEventList = ['unmount', 'appstate-change'];
1320
1332
  /**
1321
1333
  * Format event name
1322
1334
  * @param type event name
1323
1335
  * @param microWindow micro window
1324
1336
  */
1325
1337
  function formatEventType(type, microWindow) {
1326
- if (type === 'unmount') {
1327
- return `unmount-${microWindow.__MICRO_APP_NAME__}`;
1338
+ if (formatEventList.includes(type)) {
1339
+ return `${type}-${microWindow.__MICRO_APP_NAME__}`;
1328
1340
  }
1329
1341
  return type;
1330
1342
  }
@@ -2076,11 +2088,15 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2076
2088
  element.dispatchEvent(event);
2077
2089
  }
2078
2090
  /**
2079
- * Dispatch unmount event to micro app
2080
- * @param appName app.name
2091
+ * Dispatch custom event to micro app
2092
+ * @param eventName event name
2093
+ * @param appName app name
2094
+ * @param detail event detail
2081
2095
  */
2082
- function dispatchUnmountToMicroApp(appName) {
2083
- const event = new CustomEvent(`unmount-${appName}`);
2096
+ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2097
+ const event = new CustomEvent(`${eventName}-${appName}`, {
2098
+ detail,
2099
+ });
2084
2100
  window.dispatchEvent(event);
2085
2101
  }
2086
2102
 
@@ -2088,7 +2104,9 @@ function dispatchUnmountToMicroApp(appName) {
2088
2104
  const appInstanceMap = new Map();
2089
2105
  class CreateApp {
2090
2106
  constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, macro, baseroute, }) {
2091
- this.status = appStatus.NOT_LOADED;
2107
+ this.state = appStates.NOT_LOADED;
2108
+ this.keepAliveState = null;
2109
+ this.keepAliveContainer = null;
2092
2110
  this.loadSourceLevel = 0;
2093
2111
  this.umdHookMount = null;
2094
2112
  this.umdHookUnmount = null;
@@ -2118,7 +2136,7 @@ class CreateApp {
2118
2136
  }
2119
2137
  // Load resources
2120
2138
  loadSourceCode() {
2121
- this.status = appStatus.LOADING_SOURCE_CODE;
2139
+ this.state = appStates.LOADING_SOURCE_CODE;
2122
2140
  extractHtml(this);
2123
2141
  }
2124
2142
  /**
@@ -2127,9 +2145,9 @@ class CreateApp {
2127
2145
  onLoad(html) {
2128
2146
  if (++this.loadSourceLevel === 2) {
2129
2147
  this.source.html = html;
2130
- if (this.isPrefetch || appStatus.UNMOUNT === this.status)
2148
+ if (this.isPrefetch || appStates.UNMOUNT === this.state)
2131
2149
  return;
2132
- this.status = appStatus.LOAD_SOURCE_FINISHED;
2150
+ this.state = appStates.LOAD_SOURCE_FINISHED;
2133
2151
  this.mount();
2134
2152
  }
2135
2153
  }
@@ -2139,9 +2157,9 @@ class CreateApp {
2139
2157
  */
2140
2158
  onLoadError(e) {
2141
2159
  this.loadSourceLevel = -1;
2142
- if (appStatus.UNMOUNT !== this.status) {
2160
+ if (appStates.UNMOUNT !== this.state) {
2143
2161
  this.onerror(e);
2144
- this.status = appStatus.LOAD_SOURCE_ERROR;
2162
+ this.state = appStates.LOAD_SOURCE_ERROR;
2145
2163
  }
2146
2164
  }
2147
2165
  /**
@@ -2158,11 +2176,11 @@ class CreateApp {
2158
2176
  this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
2159
2177
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
2160
2178
  if (this.loadSourceLevel !== 2) {
2161
- this.status = appStatus.LOADING_SOURCE_CODE;
2179
+ this.state = appStates.LOADING_SOURCE_CODE;
2162
2180
  return;
2163
2181
  }
2164
2182
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
2165
- this.status = appStatus.MOUNTING;
2183
+ this.state = appStates.MOUNTING;
2166
2184
  cloneContainer(this.source.html, this.container, !this.umdMode);
2167
2185
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
2168
2186
  let umdHookMountResult; // result of mount function
@@ -2222,20 +2240,23 @@ class CreateApp {
2222
2240
  * dispatch mounted event when app run finished
2223
2241
  */
2224
2242
  dispatchMountedEvent() {
2225
- if (appStatus.UNMOUNT !== this.status) {
2226
- this.status = appStatus.MOUNTED;
2243
+ if (appStates.UNMOUNT !== this.state) {
2244
+ this.state = appStates.MOUNTED;
2227
2245
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
2228
2246
  }
2229
2247
  }
2230
2248
  /**
2231
2249
  * unmount app
2232
2250
  * @param destroy completely destroy, delete cache resources
2251
+ * @param unmountcb callback of unmount
2233
2252
  */
2234
- unmount(destroy) {
2235
- if (this.status === appStatus.LOAD_SOURCE_ERROR) {
2253
+ unmount(destroy, unmountcb) {
2254
+ if (this.state === appStates.LOAD_SOURCE_ERROR) {
2236
2255
  destroy = true;
2237
2256
  }
2238
- this.status = appStatus.UNMOUNT;
2257
+ this.state = appStates.UNMOUNT;
2258
+ this.keepAliveState = null;
2259
+ this.keepAliveContainer = null;
2239
2260
  // result of unmount function
2240
2261
  let umdHookUnmountResult;
2241
2262
  /**
@@ -2251,28 +2272,31 @@ class CreateApp {
2251
2272
  }
2252
2273
  }
2253
2274
  // dispatch unmount event to micro app
2254
- dispatchUnmountToMicroApp(this.name);
2255
- this.handleUnmounted(destroy, umdHookUnmountResult);
2275
+ dispatchCustomEventToMicroApp('unmount', this.name);
2276
+ this.handleUnmounted(destroy, umdHookUnmountResult, unmountcb);
2256
2277
  }
2257
2278
  /**
2258
2279
  * handle for promise umdHookUnmount
2280
+ * @param destroy completely destroy, delete cache resources
2259
2281
  * @param umdHookUnmountResult result of umdHookUnmount
2282
+ * @param unmountcb callback of unmount
2260
2283
  */
2261
- handleUnmounted(destroy, umdHookUnmountResult) {
2284
+ handleUnmounted(destroy, umdHookUnmountResult, unmountcb) {
2262
2285
  if (isPromise(umdHookUnmountResult)) {
2263
2286
  umdHookUnmountResult
2264
- .then(() => this.actionsForUnmount(destroy))
2265
- .catch(() => this.actionsForUnmount(destroy));
2287
+ .then(() => this.actionsForUnmount(destroy, unmountcb))
2288
+ .catch(() => this.actionsForUnmount(destroy, unmountcb));
2266
2289
  }
2267
2290
  else {
2268
- this.actionsForUnmount(destroy);
2291
+ this.actionsForUnmount(destroy, unmountcb);
2269
2292
  }
2270
2293
  }
2271
2294
  /**
2272
2295
  * actions for unmount app
2273
2296
  * @param destroy completely destroy, delete cache resources
2297
+ * @param unmountcb callback of unmount
2274
2298
  */
2275
- actionsForUnmount(destroy) {
2299
+ actionsForUnmount(destroy, unmountcb) {
2276
2300
  var _a;
2277
2301
  // dispatch unmount event to base app
2278
2302
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
@@ -2281,12 +2305,11 @@ class CreateApp {
2281
2305
  this.actionsForCompletelyDestory();
2282
2306
  }
2283
2307
  else if (this.umdMode && this.container.childElementCount) {
2284
- /**
2285
- * In umd mode, ui frameworks will no longer create style elements to head in lazy load page when render again, so we should save container to keep these elements
2286
- */
2287
2308
  cloneContainer(this.container, this.source.html, false);
2288
2309
  }
2310
+ this.container.innerHTML = '';
2289
2311
  this.container = null;
2312
+ unmountcb && unmountcb();
2290
2313
  }
2291
2314
  // actions for completely destroy
2292
2315
  actionsForCompletelyDestory() {
@@ -2295,6 +2318,37 @@ class CreateApp {
2295
2318
  }
2296
2319
  appInstanceMap.delete(this.name);
2297
2320
  }
2321
+ // hidden app when disconnectedCallback called with keep-alive
2322
+ hiddenKeepAliveApp() {
2323
+ this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
2324
+ // event should dispatch before clone node
2325
+ // dispatch afterhidden event to micro-app
2326
+ dispatchCustomEventToMicroApp('appstate-change', this.name, {
2327
+ appState: 'afterhidden',
2328
+ });
2329
+ // dispatch afterhidden event to base app
2330
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERHIDDEN);
2331
+ cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
2332
+ this.container = this.keepAliveContainer;
2333
+ }
2334
+ // show app when connectedCallback called with keep-alive
2335
+ showKeepAliveApp(container) {
2336
+ // dispatch beforeshow event to micro-app
2337
+ dispatchCustomEventToMicroApp('appstate-change', this.name, {
2338
+ appState: 'beforeshow',
2339
+ });
2340
+ // dispatch beforeshow event to base app
2341
+ dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
2342
+ cloneContainer(this.container, container, false);
2343
+ this.container = container;
2344
+ this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
2345
+ // dispatch aftershow event to micro-app
2346
+ dispatchCustomEventToMicroApp('appstate-change', this.name, {
2347
+ appState: 'aftershow',
2348
+ });
2349
+ // dispatch aftershow event to base app
2350
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
2351
+ }
2298
2352
  /**
2299
2353
  * app rendering error
2300
2354
  * @param e Error
@@ -2302,15 +2356,19 @@ class CreateApp {
2302
2356
  onerror(e) {
2303
2357
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
2304
2358
  }
2305
- // get app status
2306
- getAppStatus() {
2307
- return this.status;
2359
+ // get app state
2360
+ getAppState() {
2361
+ return this.state;
2362
+ }
2363
+ // get keep-alive state
2364
+ getKeepAliveState() {
2365
+ return this.keepAliveState;
2308
2366
  }
2309
2367
  // get umd library, if it not exist, return empty object
2310
2368
  getUmdLibraryHooks() {
2311
2369
  var _a, _b;
2312
2370
  // after execScripts, the app maybe unmounted
2313
- if (appStatus.UNMOUNT !== this.status) {
2371
+ if (appStates.UNMOUNT !== this.state) {
2314
2372
  const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
2315
2373
  this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
2316
2374
  // do not use isObject
@@ -2323,7 +2381,7 @@ class CreateApp {
2323
2381
  function getActiveApps() {
2324
2382
  const activeApps = [];
2325
2383
  appInstanceMap.forEach((app, appName) => {
2326
- if (appStatus.UNMOUNT !== app.getAppStatus() && !app.isPrefetch) {
2384
+ if (appStates.UNMOUNT !== app.getAppState() && !app.isPrefetch) {
2327
2385
  activeApps.push(appName);
2328
2386
  }
2329
2387
  });
@@ -2700,7 +2758,7 @@ function rejectMicroAppStyle() {
2700
2758
  }
2701
2759
 
2702
2760
  function unmountNestedApp() {
2703
- replaseUnmountOfNestedApp();
2761
+ releaseUnmountOfNestedApp();
2704
2762
  appInstanceMap.forEach(app => {
2705
2763
  // @ts-ignore
2706
2764
  app.container && getRootContainer(app.container).disconnectedCallback();
@@ -2718,7 +2776,7 @@ function listenUmountOfNestedApp() {
2718
2776
  }
2719
2777
  }
2720
2778
  // release listener
2721
- function replaseUnmountOfNestedApp() {
2779
+ function releaseUnmountOfNestedApp() {
2722
2780
  if (window.__MICRO_APP_ENVIRONMENT__) {
2723
2781
  window.removeEventListener('unmount', unmountNestedApp, false);
2724
2782
  }
@@ -2745,7 +2803,6 @@ function defineElement(tagName) {
2745
2803
  * handle for change of name an url after element inited
2746
2804
  */
2747
2805
  this.handleAttributeUpdate = () => {
2748
- var _a;
2749
2806
  this.isWating = false;
2750
2807
  const formatAttrName = formatAppName(this.getAttribute('name'));
2751
2808
  const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
@@ -2753,44 +2810,27 @@ function defineElement(tagName) {
2753
2810
  const existApp = appInstanceMap.get(formatAttrName);
2754
2811
  if (formatAttrName !== this.appName && existApp) {
2755
2812
  // handling of cached and non-prefetch apps
2756
- if (appStatus.UNMOUNT !== existApp.getAppStatus() && !existApp.isPrefetch) {
2813
+ if (appStates.UNMOUNT !== existApp.getAppState() &&
2814
+ keepAliveStates.KEEP_ALIVE_HIDDEN !== existApp.getKeepAliveState() &&
2815
+ !existApp.isPrefetch) {
2757
2816
  this.setAttribute('name', this.appName);
2758
- return logError(`an app named ${formatAttrName} already exists`, this.appName);
2817
+ return logError(`app name conflict, an app named ${formatAttrName} is running`, this.appName);
2759
2818
  }
2760
2819
  }
2761
2820
  if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
2762
- this.handleUnmount(formatAttrName === this.appName);
2763
- /**
2764
- * change ssrUrl in ssr mode
2765
- * do not add judgment of formatAttrUrl === this.appUrl
2766
- */
2767
- if (this.getDisposeResult('ssr')) {
2768
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
2769
- }
2770
- else if (this.ssrUrl) {
2771
- this.ssrUrl = '';
2772
- }
2773
- this.appName = formatAttrName;
2774
- this.appUrl = formatAttrUrl;
2775
- ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
2776
- if (formatAttrName !== this.getAttribute('name')) {
2777
- this.setAttribute('name', this.appName);
2821
+ if (formatAttrName === this.appName) {
2822
+ this.handleUnmount(true, () => {
2823
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2824
+ });
2778
2825
  }
2779
- /**
2780
- * when existApp not null:
2781
- * scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, existApp has been unmounted
2782
- * scene2: if formatAttrName and this.appName are different: existApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace existApp
2783
- * scene3: url is different but ssrUrl is equal
2784
- * scene4: url is equal but ssrUrl is different, if url is equal, name must different
2785
- */
2786
- if (existApp &&
2787
- existApp.url === this.appUrl &&
2788
- existApp.ssrUrl === this.ssrUrl) {
2789
- // mount app
2790
- this.handleAppMount(existApp);
2826
+ else if (this.getDisposeResult('keep-alive')) {
2827
+ this.handleHiddenKeepAliveApp();
2828
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2791
2829
  }
2792
2830
  else {
2793
- this.handleCreateApp();
2831
+ this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'), () => {
2832
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2833
+ });
2794
2834
  }
2795
2835
  }
2796
2836
  }
@@ -2816,6 +2856,7 @@ function defineElement(tagName) {
2816
2856
  // disableSandbox: whether disable sandbox, default is false
2817
2857
  // macro: used to solve the async render problem of vue3, default is false
2818
2858
  // baseRoute: route prefix, default is ''
2859
+ // keep-alive: open keep-alive mode, keep-alive has priority over destroy
2819
2860
  connectedCallback() {
2820
2861
  this.hasConnected = true;
2821
2862
  if (!elementInstanceMap.has(this)) {
@@ -2826,10 +2867,17 @@ function defineElement(tagName) {
2826
2867
  }
2827
2868
  disconnectedCallback() {
2828
2869
  this.hasConnected = false;
2829
- elementInstanceMap.delete(this);
2830
- this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'));
2831
- if (elementInstanceMap.size === 0) {
2832
- releasePatches();
2870
+ // keep-alive has priority over destroy
2871
+ if (this.getDisposeResult('keep-alive')) {
2872
+ this.handleHiddenKeepAliveApp();
2873
+ }
2874
+ else {
2875
+ elementInstanceMap.delete(this);
2876
+ this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'), () => {
2877
+ if (elementInstanceMap.size === 0) {
2878
+ releasePatches();
2879
+ }
2880
+ });
2833
2881
  }
2834
2882
  }
2835
2883
  attributeChangedCallback(attr, _oldVal, newVal) {
@@ -2873,7 +2921,7 @@ function defineElement(tagName) {
2873
2921
  if (elementInstanceMap.set(this, true).size === 1) {
2874
2922
  patchElementPrototypeMethods();
2875
2923
  rejectMicroAppStyle();
2876
- replaseUnmountOfNestedApp();
2924
+ releaseUnmountOfNestedApp();
2877
2925
  listenUmountOfNestedApp();
2878
2926
  }
2879
2927
  }
@@ -2892,15 +2940,21 @@ function defineElement(tagName) {
2892
2940
  else if (this.ssrUrl) {
2893
2941
  this.ssrUrl = '';
2894
2942
  }
2895
- const app = appInstanceMap.get(this.appName);
2896
- if (app) {
2943
+ if (appInstanceMap.has(this.appName)) {
2944
+ const app = appInstanceMap.get(this.appName);
2897
2945
  const existAppUrl = app.ssrUrl || app.url;
2898
2946
  const activeAppUrl = this.ssrUrl || this.appUrl;
2899
- if (existAppUrl === activeAppUrl && (app.isPrefetch ||
2900
- app.getAppStatus() === appStatus.UNMOUNT)) {
2947
+ // keep-alive don't care about ssrUrl
2948
+ // Even if the keep-alive app is pushed into the background, it is still active and cannot be replaced. Otherwise, it is difficult for developers to troubleshoot in case of conflict and will leave developers at a loss
2949
+ if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
2950
+ app.url === this.appUrl) {
2951
+ this.handleShowKeepAliveApp(app);
2952
+ }
2953
+ else if (existAppUrl === activeAppUrl && (app.isPrefetch ||
2954
+ app.getAppState() === appStates.UNMOUNT)) {
2901
2955
  this.handleAppMount(app);
2902
2956
  }
2903
- else if (app.isPrefetch || app.getAppStatus() === appStatus.UNMOUNT) {
2957
+ else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
2904
2958
  /**
2905
2959
  * url is different & old app is unmounted or prefetch, create new app to replace old one
2906
2960
  */
@@ -2908,7 +2962,56 @@ function defineElement(tagName) {
2908
2962
  this.handleCreateApp();
2909
2963
  }
2910
2964
  else {
2911
- logError(`an app named ${this.appName} already exists`, this.appName);
2965
+ logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
2966
+ }
2967
+ }
2968
+ else {
2969
+ this.handleCreateApp();
2970
+ }
2971
+ }
2972
+ // remount app or create app if attribute url or name change
2973
+ actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp) {
2974
+ var _a;
2975
+ /**
2976
+ * change ssrUrl in ssr mode
2977
+ * do not add judgment of formatAttrUrl === this.appUrl
2978
+ */
2979
+ if (this.getDisposeResult('ssr')) {
2980
+ this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
2981
+ }
2982
+ else if (this.ssrUrl) {
2983
+ this.ssrUrl = '';
2984
+ }
2985
+ this.appName = formatAttrName;
2986
+ this.appUrl = formatAttrUrl;
2987
+ ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
2988
+ if (formatAttrName !== this.getAttribute('name')) {
2989
+ this.setAttribute('name', this.appName);
2990
+ }
2991
+ /**
2992
+ * when existApp not null: this.appName === existApp.name
2993
+ * scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, existApp has been unmounted
2994
+ * scene2: if formatAttrName and this.appName are different: existApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace existApp
2995
+ * scene3: url is different but ssrUrl is equal
2996
+ * scene4: url is equal but ssrUrl is different, if url is equal, name must different
2997
+ * scene5: if existApp is KEEP_ALIVE_HIDDEN, name must different
2998
+ */
2999
+ if (existApp) {
3000
+ if (existApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
3001
+ if (existApp.url === this.appUrl) {
3002
+ this.handleShowKeepAliveApp(existApp);
3003
+ }
3004
+ else {
3005
+ // the hidden keep-alive app is still active
3006
+ logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
3007
+ }
3008
+ }
3009
+ else if (existApp.url === this.appUrl && existApp.ssrUrl === this.ssrUrl) {
3010
+ // mount app
3011
+ this.handleAppMount(existApp);
3012
+ }
3013
+ else {
3014
+ this.handleCreateApp();
2912
3015
  }
2913
3016
  }
2914
3017
  else {
@@ -2968,10 +3071,24 @@ function defineElement(tagName) {
2968
3071
  * unmount app
2969
3072
  * @param destroy delete cache resources when unmount
2970
3073
  */
2971
- handleUnmount(destroy) {
3074
+ handleUnmount(destroy, unmountcb) {
3075
+ const app = appInstanceMap.get(this.appName);
3076
+ if (app &&
3077
+ app.getAppState() !== appStates.UNMOUNT)
3078
+ app.unmount(destroy, unmountcb);
3079
+ }
3080
+ // hidden app when disconnectedCallback called with keep-alive
3081
+ handleHiddenKeepAliveApp() {
2972
3082
  const app = appInstanceMap.get(this.appName);
2973
- if (app && appStatus.UNMOUNT !== app.getAppStatus())
2974
- app.unmount(destroy);
3083
+ if (app &&
3084
+ app.getAppState() !== appStates.UNMOUNT &&
3085
+ app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN)
3086
+ app.hiddenKeepAliveApp();
3087
+ }
3088
+ // show app when connectedCallback called with keep-alive
3089
+ handleShowKeepAliveApp(app) {
3090
+ // must be asnyc
3091
+ defer(() => { var _a; return app.showKeepAliveApp((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this); });
2975
3092
  }
2976
3093
  /**
2977
3094
  * Get configuration