@micro-zoe/micro-app 0.5.0 → 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.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '0.5.0';
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
@@ -625,7 +635,7 @@ const globalLinks = new Map();
625
635
  * @param microAppHead micro-app-head element
626
636
  * @param isDynamic dynamic insert
627
637
  */
628
- function extractLinkFromHtml(link, parent, app, microAppHead, isDynamic = false) {
638
+ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
629
639
  const rel = link.getAttribute('rel');
630
640
  let href = link.getAttribute('href');
631
641
  let replaceComment = null;
@@ -633,12 +643,9 @@ function extractLinkFromHtml(link, parent, app, microAppHead, isDynamic = false)
633
643
  href = CompletionPath(href, app.url);
634
644
  if (!isDynamic) {
635
645
  replaceComment = document.createComment(`link element with href=${href} move to micro-app-head as style element`);
636
- const placeholderComment = document.createComment(`placeholder for link with href=${href}`);
637
- // all style elements insert into microAppHead
638
- microAppHead.appendChild(placeholderComment);
639
646
  app.source.links.set(href, {
640
647
  code: '',
641
- placeholder: placeholderComment,
648
+ placeholder: replaceComment,
642
649
  isGlobal: link.hasAttribute('global'),
643
650
  });
644
651
  }
@@ -653,7 +660,7 @@ function extractLinkFromHtml(link, parent, app, microAppHead, isDynamic = false)
653
660
  }
654
661
  }
655
662
  else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
656
- // preload prefetch icon ....
663
+ // preload prefetch icon ....
657
664
  if (isDynamic) {
658
665
  replaceComment = document.createComment(`link element with rel=${rel}${href ? ' & href=' + href : ''} removed by micro-app`);
659
666
  }
@@ -708,6 +715,7 @@ function fetchLinkSuccess(url, info, data, microAppHead, app) {
708
715
  const styleLink = pureCreateElement('style');
709
716
  styleLink.textContent = data;
710
717
  styleLink.__MICRO_APP_LINK_PATH__ = url;
718
+ styleLink.setAttribute('data-origin-href', url);
711
719
  microAppHead.replaceChild(scopedCSS(styleLink, app), info.placeholder);
712
720
  info.placeholder = null;
713
721
  info.code = data;
@@ -1012,9 +1020,6 @@ function runCode2InlineScript(url, code, module, scriptElement, callback) {
1012
1020
  const blob = new Blob([code], { type: 'text/javascript' });
1013
1021
  scriptElement.src = URL.createObjectURL(blob);
1014
1022
  scriptElement.setAttribute('type', 'module');
1015
- if (!url.startsWith('inline-')) {
1016
- scriptElement.setAttribute('originSrc', url);
1017
- }
1018
1023
  if (callback) {
1019
1024
  callback.moduleCount && callback.moduleCount--;
1020
1025
  scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
@@ -1023,6 +1028,9 @@ function runCode2InlineScript(url, code, module, scriptElement, callback) {
1023
1028
  else {
1024
1029
  scriptElement.textContent = code;
1025
1030
  }
1031
+ if (!url.startsWith('inline-')) {
1032
+ scriptElement.setAttribute('data-origin-src', url);
1033
+ }
1026
1034
  }
1027
1035
  // init & run code2Function
1028
1036
  function runCode2Function(code, info) {
@@ -1092,7 +1100,7 @@ function getWrapElement(str) {
1092
1100
  function flatChildren(parent, app, microAppHead) {
1093
1101
  const children = Array.from(parent.children);
1094
1102
  children.length && children.forEach((child) => {
1095
- flatChildren(child, app, microAppHead);
1103
+ flatChildren(child, app);
1096
1104
  });
1097
1105
  for (const dom of children) {
1098
1106
  if (dom instanceof HTMLLinkElement) {
@@ -1100,7 +1108,7 @@ function flatChildren(parent, app, microAppHead) {
1100
1108
  parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
1101
1109
  }
1102
1110
  else if (!dom.hasAttribute('ignore')) {
1103
- extractLinkFromHtml(dom, parent, app, microAppHead);
1111
+ extractLinkFromHtml(dom, parent, app);
1104
1112
  }
1105
1113
  else if (dom.hasAttribute('href')) {
1106
1114
  dom.setAttribute('href', CompletionPath(dom.getAttribute('href'), app.url));
@@ -1111,7 +1119,7 @@ function flatChildren(parent, app, microAppHead) {
1111
1119
  parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
1112
1120
  }
1113
1121
  else if (app.scopecss && !dom.hasAttribute('ignore')) {
1114
- microAppHead.appendChild(scopedCSS(dom, app));
1122
+ scopedCSS(dom, app);
1115
1123
  }
1116
1124
  }
1117
1125
  else if (dom instanceof HTMLScriptElement) {
@@ -1139,7 +1147,7 @@ function extractSourceDom(htmlStr, app) {
1139
1147
  app.onerror(new Error(msg));
1140
1148
  return logError(msg, app.name);
1141
1149
  }
1142
- flatChildren(wrapElement, app, microAppHead);
1150
+ flatChildren(wrapElement, app);
1143
1151
  if (app.source.links.size) {
1144
1152
  fetchLinksFromHtml(wrapElement, app, microAppHead);
1145
1153
  }
@@ -1319,14 +1327,16 @@ function releaseEffectDocumentEvent() {
1319
1327
  document.addEventListener = globalEnv.rawDocumentAddEventListener;
1320
1328
  document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
1321
1329
  }
1330
+ // this events should be sent to the specified app
1331
+ const formatEventList = ['unmount', 'appstate-change'];
1322
1332
  /**
1323
1333
  * Format event name
1324
1334
  * @param type event name
1325
1335
  * @param microWindow micro window
1326
1336
  */
1327
1337
  function formatEventType(type, microWindow) {
1328
- if (type === 'unmount') {
1329
- return `unmount-${microWindow.__MICRO_APP_NAME__}`;
1338
+ if (formatEventList.includes(type)) {
1339
+ return `${type}-${microWindow.__MICRO_APP_NAME__}`;
1330
1340
  }
1331
1341
  return type;
1332
1342
  }
@@ -1890,12 +1900,15 @@ class SandBox {
1890
1900
  return key in target;
1891
1901
  return key in unscopables || key in target || key in rawWindow;
1892
1902
  },
1903
+ // Object.getOwnPropertyDescriptor(window, key)
1904
+ // TODO: use set
1893
1905
  getOwnPropertyDescriptor: (target, key) => {
1894
1906
  if (target.hasOwnProperty(key)) {
1895
1907
  descriptorTargetMap.set(key, 'target');
1896
1908
  return Object.getOwnPropertyDescriptor(target, key);
1897
1909
  }
1898
1910
  if (rawWindow.hasOwnProperty(key)) {
1911
+ // like console, alert ...
1899
1912
  descriptorTargetMap.set(key, 'rawWindow');
1900
1913
  const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1901
1914
  if (descriptor && !descriptor.configurable) {
@@ -1905,6 +1918,7 @@ class SandBox {
1905
1918
  }
1906
1919
  return undefined;
1907
1920
  },
1921
+ // Object.defineProperty(window, key, Descriptor)
1908
1922
  defineProperty: (target, key, value) => {
1909
1923
  const from = descriptorTargetMap.get(key);
1910
1924
  if (from === 'rawWindow') {
@@ -1912,11 +1926,13 @@ class SandBox {
1912
1926
  }
1913
1927
  return Reflect.defineProperty(target, key, value);
1914
1928
  },
1929
+ // Object.getOwnPropertyNames(window)
1915
1930
  ownKeys: (target) => {
1916
1931
  return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
1917
1932
  },
1918
1933
  deleteProperty: (target, key) => {
1919
1934
  if (target.hasOwnProperty(key)) {
1935
+ this.injectedKeys.has(key) && this.injectedKeys.delete(key);
1920
1936
  this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
1921
1937
  return Reflect.deleteProperty(target, key);
1922
1938
  }
@@ -2072,11 +2088,15 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2072
2088
  element.dispatchEvent(event);
2073
2089
  }
2074
2090
  /**
2075
- * Dispatch unmount event to micro app
2076
- * @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
2077
2095
  */
2078
- function dispatchUnmountToMicroApp(appName) {
2079
- const event = new CustomEvent(`unmount-${appName}`);
2096
+ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2097
+ const event = new CustomEvent(`${eventName}-${appName}`, {
2098
+ detail,
2099
+ });
2080
2100
  window.dispatchEvent(event);
2081
2101
  }
2082
2102
 
@@ -2084,7 +2104,9 @@ function dispatchUnmountToMicroApp(appName) {
2084
2104
  const appInstanceMap = new Map();
2085
2105
  class CreateApp {
2086
2106
  constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, macro, baseroute, }) {
2087
- this.status = appStatus.NOT_LOADED;
2107
+ this.state = appStates.NOT_LOADED;
2108
+ this.keepAliveState = null;
2109
+ this.keepAliveContainer = null;
2088
2110
  this.loadSourceLevel = 0;
2089
2111
  this.umdHookMount = null;
2090
2112
  this.umdHookUnmount = null;
@@ -2114,7 +2136,7 @@ class CreateApp {
2114
2136
  }
2115
2137
  // Load resources
2116
2138
  loadSourceCode() {
2117
- this.status = appStatus.LOADING_SOURCE_CODE;
2139
+ this.state = appStates.LOADING_SOURCE_CODE;
2118
2140
  extractHtml(this);
2119
2141
  }
2120
2142
  /**
@@ -2123,9 +2145,9 @@ class CreateApp {
2123
2145
  onLoad(html) {
2124
2146
  if (++this.loadSourceLevel === 2) {
2125
2147
  this.source.html = html;
2126
- if (this.isPrefetch || appStatus.UNMOUNT === this.status)
2148
+ if (this.isPrefetch || appStates.UNMOUNT === this.state)
2127
2149
  return;
2128
- this.status = appStatus.LOAD_SOURCE_FINISHED;
2150
+ this.state = appStates.LOAD_SOURCE_FINISHED;
2129
2151
  this.mount();
2130
2152
  }
2131
2153
  }
@@ -2135,9 +2157,9 @@ class CreateApp {
2135
2157
  */
2136
2158
  onLoadError(e) {
2137
2159
  this.loadSourceLevel = -1;
2138
- if (appStatus.UNMOUNT !== this.status) {
2160
+ if (appStates.UNMOUNT !== this.state) {
2139
2161
  this.onerror(e);
2140
- this.status = appStatus.LOAD_SOURCE_ERROR;
2162
+ this.state = appStates.LOAD_SOURCE_ERROR;
2141
2163
  }
2142
2164
  }
2143
2165
  /**
@@ -2154,11 +2176,11 @@ class CreateApp {
2154
2176
  this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
2155
2177
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
2156
2178
  if (this.loadSourceLevel !== 2) {
2157
- this.status = appStatus.LOADING_SOURCE_CODE;
2179
+ this.state = appStates.LOADING_SOURCE_CODE;
2158
2180
  return;
2159
2181
  }
2160
2182
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
2161
- this.status = appStatus.MOUNTING;
2183
+ this.state = appStates.MOUNTING;
2162
2184
  cloneContainer(this.source.html, this.container, !this.umdMode);
2163
2185
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
2164
2186
  let umdHookMountResult; // result of mount function
@@ -2218,20 +2240,23 @@ class CreateApp {
2218
2240
  * dispatch mounted event when app run finished
2219
2241
  */
2220
2242
  dispatchMountedEvent() {
2221
- if (appStatus.UNMOUNT !== this.status) {
2222
- this.status = appStatus.MOUNTED;
2243
+ if (appStates.UNMOUNT !== this.state) {
2244
+ this.state = appStates.MOUNTED;
2223
2245
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
2224
2246
  }
2225
2247
  }
2226
2248
  /**
2227
2249
  * unmount app
2228
2250
  * @param destroy completely destroy, delete cache resources
2251
+ * @param unmountcb callback of unmount
2229
2252
  */
2230
- unmount(destroy) {
2231
- if (this.status === appStatus.LOAD_SOURCE_ERROR) {
2253
+ unmount(destroy, unmountcb) {
2254
+ if (this.state === appStates.LOAD_SOURCE_ERROR) {
2232
2255
  destroy = true;
2233
2256
  }
2234
- this.status = appStatus.UNMOUNT;
2257
+ this.state = appStates.UNMOUNT;
2258
+ this.keepAliveState = null;
2259
+ this.keepAliveContainer = null;
2235
2260
  // result of unmount function
2236
2261
  let umdHookUnmountResult;
2237
2262
  /**
@@ -2247,28 +2272,31 @@ class CreateApp {
2247
2272
  }
2248
2273
  }
2249
2274
  // dispatch unmount event to micro app
2250
- dispatchUnmountToMicroApp(this.name);
2251
- this.handleUnmounted(destroy, umdHookUnmountResult);
2275
+ dispatchCustomEventToMicroApp('unmount', this.name);
2276
+ this.handleUnmounted(destroy, umdHookUnmountResult, unmountcb);
2252
2277
  }
2253
2278
  /**
2254
2279
  * handle for promise umdHookUnmount
2280
+ * @param destroy completely destroy, delete cache resources
2255
2281
  * @param umdHookUnmountResult result of umdHookUnmount
2282
+ * @param unmountcb callback of unmount
2256
2283
  */
2257
- handleUnmounted(destroy, umdHookUnmountResult) {
2284
+ handleUnmounted(destroy, umdHookUnmountResult, unmountcb) {
2258
2285
  if (isPromise(umdHookUnmountResult)) {
2259
2286
  umdHookUnmountResult
2260
- .then(() => this.actionsForUnmount(destroy))
2261
- .catch(() => this.actionsForUnmount(destroy));
2287
+ .then(() => this.actionsForUnmount(destroy, unmountcb))
2288
+ .catch(() => this.actionsForUnmount(destroy, unmountcb));
2262
2289
  }
2263
2290
  else {
2264
- this.actionsForUnmount(destroy);
2291
+ this.actionsForUnmount(destroy, unmountcb);
2265
2292
  }
2266
2293
  }
2267
2294
  /**
2268
2295
  * actions for unmount app
2269
2296
  * @param destroy completely destroy, delete cache resources
2297
+ * @param unmountcb callback of unmount
2270
2298
  */
2271
- actionsForUnmount(destroy) {
2299
+ actionsForUnmount(destroy, unmountcb) {
2272
2300
  var _a;
2273
2301
  // dispatch unmount event to base app
2274
2302
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
@@ -2277,12 +2305,11 @@ class CreateApp {
2277
2305
  this.actionsForCompletelyDestory();
2278
2306
  }
2279
2307
  else if (this.umdMode && this.container.childElementCount) {
2280
- /**
2281
- * 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
2282
- */
2283
2308
  cloneContainer(this.container, this.source.html, false);
2284
2309
  }
2310
+ this.container.innerHTML = '';
2285
2311
  this.container = null;
2312
+ unmountcb && unmountcb();
2286
2313
  }
2287
2314
  // actions for completely destroy
2288
2315
  actionsForCompletelyDestory() {
@@ -2291,6 +2318,37 @@ class CreateApp {
2291
2318
  }
2292
2319
  appInstanceMap.delete(this.name);
2293
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
+ }
2294
2352
  /**
2295
2353
  * app rendering error
2296
2354
  * @param e Error
@@ -2298,15 +2356,19 @@ class CreateApp {
2298
2356
  onerror(e) {
2299
2357
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
2300
2358
  }
2301
- // get app status
2302
- getAppStatus() {
2303
- 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;
2304
2366
  }
2305
2367
  // get umd library, if it not exist, return empty object
2306
2368
  getUmdLibraryHooks() {
2307
2369
  var _a, _b;
2308
2370
  // after execScripts, the app maybe unmounted
2309
- if (appStatus.UNMOUNT !== this.status) {
2371
+ if (appStates.UNMOUNT !== this.state) {
2310
2372
  const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
2311
2373
  this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
2312
2374
  // do not use isObject
@@ -2315,6 +2377,20 @@ class CreateApp {
2315
2377
  return {};
2316
2378
  }
2317
2379
  }
2380
+ // if app not prefetch & not unmount, then app is active
2381
+ function getActiveApps() {
2382
+ const activeApps = [];
2383
+ appInstanceMap.forEach((app, appName) => {
2384
+ if (appStates.UNMOUNT !== app.getAppState() && !app.isPrefetch) {
2385
+ activeApps.push(appName);
2386
+ }
2387
+ });
2388
+ return activeApps;
2389
+ }
2390
+ // get all registered apps
2391
+ function getAllApps() {
2392
+ return Array.from(appInstanceMap.keys());
2393
+ }
2318
2394
 
2319
2395
  // Record element and map element
2320
2396
  const dynamicElementInMicroAppMap = new WeakMap();
@@ -2345,7 +2421,7 @@ function handleNewNode(parent, child, app) {
2345
2421
  else if (child.hasAttribute('ignore')) {
2346
2422
  return child;
2347
2423
  }
2348
- const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, null, true);
2424
+ const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
2349
2425
  if (url && info) {
2350
2426
  const replaceStyle = pureCreateElement('style');
2351
2427
  replaceStyle.__MICRO_APP_LINK_PATH__ = url;
@@ -2682,7 +2758,7 @@ function rejectMicroAppStyle() {
2682
2758
  }
2683
2759
 
2684
2760
  function unmountNestedApp() {
2685
- replaseUnmountOfNestedApp();
2761
+ releaseUnmountOfNestedApp();
2686
2762
  appInstanceMap.forEach(app => {
2687
2763
  // @ts-ignore
2688
2764
  app.container && getRootContainer(app.container).disconnectedCallback();
@@ -2700,7 +2776,7 @@ function listenUmountOfNestedApp() {
2700
2776
  }
2701
2777
  }
2702
2778
  // release listener
2703
- function replaseUnmountOfNestedApp() {
2779
+ function releaseUnmountOfNestedApp() {
2704
2780
  if (window.__MICRO_APP_ENVIRONMENT__) {
2705
2781
  window.removeEventListener('unmount', unmountNestedApp, false);
2706
2782
  }
@@ -2727,7 +2803,6 @@ function defineElement(tagName) {
2727
2803
  * handle for change of name an url after element inited
2728
2804
  */
2729
2805
  this.handleAttributeUpdate = () => {
2730
- var _a;
2731
2806
  this.isWating = false;
2732
2807
  const formatAttrName = formatAppName(this.getAttribute('name'));
2733
2808
  const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
@@ -2735,44 +2810,27 @@ function defineElement(tagName) {
2735
2810
  const existApp = appInstanceMap.get(formatAttrName);
2736
2811
  if (formatAttrName !== this.appName && existApp) {
2737
2812
  // handling of cached and non-prefetch apps
2738
- if (appStatus.UNMOUNT !== existApp.getAppStatus() && !existApp.isPrefetch) {
2813
+ if (appStates.UNMOUNT !== existApp.getAppState() &&
2814
+ keepAliveStates.KEEP_ALIVE_HIDDEN !== existApp.getKeepAliveState() &&
2815
+ !existApp.isPrefetch) {
2739
2816
  this.setAttribute('name', this.appName);
2740
- return logError(`an app named ${formatAttrName} already exists`, this.appName);
2817
+ return logError(`app name conflict, an app named ${formatAttrName} is running`, this.appName);
2741
2818
  }
2742
2819
  }
2743
2820
  if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
2744
- this.handleUnmount(formatAttrName === this.appName);
2745
- /**
2746
- * change ssrUrl in ssr mode
2747
- * do not add judgment of formatAttrUrl === this.appUrl
2748
- */
2749
- if (this.getDisposeResult('ssr')) {
2750
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
2751
- }
2752
- else if (this.ssrUrl) {
2753
- this.ssrUrl = '';
2754
- }
2755
- this.appName = formatAttrName;
2756
- this.appUrl = formatAttrUrl;
2757
- ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
2758
- if (formatAttrName !== this.getAttribute('name')) {
2759
- this.setAttribute('name', this.appName);
2821
+ if (formatAttrName === this.appName) {
2822
+ this.handleUnmount(true, () => {
2823
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2824
+ });
2760
2825
  }
2761
- /**
2762
- * when existApp not null:
2763
- * scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, existApp has been unmounted
2764
- * 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
2765
- * scene3: url is different but ssrUrl is equal
2766
- * scene4: url is equal but ssrUrl is different, if url is equal, name must different
2767
- */
2768
- if (existApp &&
2769
- existApp.url === this.appUrl &&
2770
- existApp.ssrUrl === this.ssrUrl) {
2771
- // mount app
2772
- this.handleAppMount(existApp);
2826
+ else if (this.getDisposeResult('keep-alive')) {
2827
+ this.handleHiddenKeepAliveApp();
2828
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2773
2829
  }
2774
2830
  else {
2775
- this.handleCreateApp();
2831
+ this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'), () => {
2832
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2833
+ });
2776
2834
  }
2777
2835
  }
2778
2836
  }
@@ -2798,6 +2856,7 @@ function defineElement(tagName) {
2798
2856
  // disableSandbox: whether disable sandbox, default is false
2799
2857
  // macro: used to solve the async render problem of vue3, default is false
2800
2858
  // baseRoute: route prefix, default is ''
2859
+ // keep-alive: open keep-alive mode, keep-alive has priority over destroy
2801
2860
  connectedCallback() {
2802
2861
  this.hasConnected = true;
2803
2862
  if (!elementInstanceMap.has(this)) {
@@ -2808,10 +2867,17 @@ function defineElement(tagName) {
2808
2867
  }
2809
2868
  disconnectedCallback() {
2810
2869
  this.hasConnected = false;
2811
- elementInstanceMap.delete(this);
2812
- this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'));
2813
- if (elementInstanceMap.size === 0) {
2814
- 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
+ });
2815
2881
  }
2816
2882
  }
2817
2883
  attributeChangedCallback(attr, _oldVal, newVal) {
@@ -2855,7 +2921,7 @@ function defineElement(tagName) {
2855
2921
  if (elementInstanceMap.set(this, true).size === 1) {
2856
2922
  patchElementPrototypeMethods();
2857
2923
  rejectMicroAppStyle();
2858
- replaseUnmountOfNestedApp();
2924
+ releaseUnmountOfNestedApp();
2859
2925
  listenUmountOfNestedApp();
2860
2926
  }
2861
2927
  }
@@ -2874,15 +2940,21 @@ function defineElement(tagName) {
2874
2940
  else if (this.ssrUrl) {
2875
2941
  this.ssrUrl = '';
2876
2942
  }
2877
- const app = appInstanceMap.get(this.appName);
2878
- if (app) {
2943
+ if (appInstanceMap.has(this.appName)) {
2944
+ const app = appInstanceMap.get(this.appName);
2879
2945
  const existAppUrl = app.ssrUrl || app.url;
2880
2946
  const activeAppUrl = this.ssrUrl || this.appUrl;
2881
- if (existAppUrl === activeAppUrl && (app.isPrefetch ||
2882
- 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)) {
2883
2955
  this.handleAppMount(app);
2884
2956
  }
2885
- else if (app.isPrefetch || app.getAppStatus() === appStatus.UNMOUNT) {
2957
+ else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
2886
2958
  /**
2887
2959
  * url is different & old app is unmounted or prefetch, create new app to replace old one
2888
2960
  */
@@ -2890,7 +2962,56 @@ function defineElement(tagName) {
2890
2962
  this.handleCreateApp();
2891
2963
  }
2892
2964
  else {
2893
- 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();
2894
3015
  }
2895
3016
  }
2896
3017
  else {
@@ -2950,10 +3071,24 @@ function defineElement(tagName) {
2950
3071
  * unmount app
2951
3072
  * @param destroy delete cache resources when unmount
2952
3073
  */
2953
- handleUnmount(destroy) {
3074
+ handleUnmount(destroy, unmountcb) {
2954
3075
  const app = appInstanceMap.get(this.appName);
2955
- if (app && appStatus.UNMOUNT !== app.getAppStatus())
2956
- app.unmount(destroy);
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() {
3082
+ const app = appInstanceMap.get(this.appName);
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); });
2957
3092
  }
2958
3093
  /**
2959
3094
  * Get configuration
@@ -3129,6 +3264,7 @@ class MicroApp extends EventCenterForBaseApp {
3129
3264
  this.disableScopecss = options.disableScopecss;
3130
3265
  this.disableSandbox = options.disableSandbox;
3131
3266
  this.macro = options.macro;
3267
+ this.ssr = options.ssr;
3132
3268
  isFunction(options.fetch) && (this.fetch = options.fetch);
3133
3269
  isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);
3134
3270
  // load app assets when browser is idle
@@ -3156,5 +3292,5 @@ class MicroApp extends EventCenterForBaseApp {
3156
3292
  var microApp = new MicroApp();
3157
3293
 
3158
3294
  export default microApp;
3159
- export { EventCenterForMicroApp, preFetch, pureCreateElement, removeDomScope, version };
3295
+ export { EventCenterForMicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, removeDomScope, version };
3160
3296
  //# sourceMappingURL=index.esm.js.map