@micro-zoe/micro-app 1.0.0-beta.2 → 1.0.0-beta.4

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 = '1.0.0-beta.2';
1
+ const version = '1.0.0-beta.4';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -356,7 +356,7 @@ function pureCreateElement(tagName, options) {
356
356
  * @param target Accept cloned elements
357
357
  * @param deep deep clone or transfer dom
358
358
  */
359
- function cloneContainer(origin, target, deep) {
359
+ function cloneContainer(target, origin, deep) {
360
360
  target.innerHTML = '';
361
361
  if (deep) {
362
362
  // TODO: ShadowRoot兼容,ShadowRoot不能直接使用cloneNode
@@ -562,7 +562,6 @@ var appStates;
562
562
  (function (appStates) {
563
563
  appStates["CREATED"] = "created";
564
564
  appStates["LOADING"] = "loading";
565
- appStates["LOADED"] = "loaded";
566
565
  appStates["LOAD_FAILED"] = "load_failed";
567
566
  appStates["MOUNTING"] = "mounting";
568
567
  appStates["MOUNTED"] = "mounted";
@@ -1501,7 +1500,7 @@ function execScripts(app, initHook) {
1501
1500
  // Notice the second render
1502
1501
  if (appSpaceData.defer || appSpaceData.async) {
1503
1502
  // TODO: defer和module彻底分开,不要混在一起
1504
- if (scriptInfo.isExternal && !scriptInfo.code && !(app.iframe && appSpaceData.module)) {
1503
+ if (scriptInfo.isExternal && !scriptInfo.code && !isTypeModule(app, scriptInfo)) {
1505
1504
  deferScriptPromise.push(fetchSource(address, app.name));
1506
1505
  }
1507
1506
  else {
@@ -1585,9 +1584,19 @@ function runScript(address, app, scriptInfo, callback, replaceElement) {
1585
1584
  appSpaceData.sandboxType = sandboxType;
1586
1585
  appSpaceData.parsedFunction = null;
1587
1586
  }
1587
+ /**
1588
+ * TODO: 优化逻辑
1589
+ * 是否是内联模式应该由外部传入,这样自外而内更加统一,逻辑更加清晰
1590
+ */
1588
1591
  if (isInlineMode(app, scriptInfo)) {
1589
1592
  const scriptElement = replaceElement || pureCreateElement('script');
1590
1593
  runCode2InlineScript(address, appSpaceData.parsedCode, isTypeModule(app, scriptInfo), scriptElement, appSpaceData.attrs, callback);
1594
+ /**
1595
+ * TODO: 优化逻辑
1596
+ * replaceElement不存在说明是初始化执行,需要主动插入script
1597
+ * 但这里的逻辑不清晰,应该明确声明是什么环境下才需要主动插入,而不是用replaceElement间接判断
1598
+ * replaceElement还有可能是注释类型(一定是在后台执行),这里的判断都是间接判断,不够直观
1599
+ */
1591
1600
  if (!replaceElement) {
1592
1601
  // TEST IGNORE
1593
1602
  const parent = app.iframe ? app.sandBox.microBody : app.querySelector('micro-app-body');
@@ -1623,7 +1632,7 @@ function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
1623
1632
  runScript(address, app, scriptInfo, dispatchScriptOnLoadEvent, replaceElement);
1624
1633
  !isTypeModule(app, scriptInfo) && dispatchScriptOnLoadEvent();
1625
1634
  };
1626
- if (scriptInfo.code || (app.iframe && scriptInfo.appSpace[app.name].module)) {
1635
+ if (scriptInfo.code || isTypeModule(app, scriptInfo)) {
1627
1636
  defer(runDynamicScript);
1628
1637
  }
1629
1638
  else {
@@ -2172,14 +2181,21 @@ function patchElementAndDocument() {
2172
2181
  }
2173
2182
  return globalEnv.rawRemoveChild.call(this, oldChild);
2174
2183
  };
2184
+ /**
2185
+ * The insertAdjacentElement method of the Element interface inserts a given element node at a given position relative to the element it is invoked upon.
2186
+ * NOTE:
2187
+ * 1. parameter 2 of insertAdjacentElement must type 'Element'
2188
+ */
2175
2189
  rawRootElement.prototype.insertAdjacentElement = function (where, element) {
2176
2190
  var _a;
2177
- if (element === null || element === void 0 ? void 0 : element.__MICRO_APP_NAME__) {
2191
+ if ((element === null || element === void 0 ? void 0 : element.__MICRO_APP_NAME__) && isElement(element)) {
2178
2192
  const app = appInstanceMap.get(element.__MICRO_APP_NAME__);
2179
2193
  if (app === null || app === void 0 ? void 0 : app.container) {
2180
- element = handleNewNode(element, app);
2181
- const realParent = (_a = getHijackParent(this, element, app)) !== null && _a !== void 0 ? _a : this;
2182
- return globalEnv.rawInsertAdjacentElement.call(realParent, where, element);
2194
+ const processedEle = handleNewNode(element, app);
2195
+ if (!isElement(processedEle))
2196
+ return element;
2197
+ const realParent = (_a = getHijackParent(this, processedEle, app)) !== null && _a !== void 0 ? _a : this;
2198
+ return globalEnv.rawInsertAdjacentElement.call(realParent, where, processedEle);
2183
2199
  }
2184
2200
  }
2185
2201
  return globalEnv.rawInsertAdjacentElement.call(this, where, element);
@@ -3294,8 +3310,12 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
3294
3310
  * @param microAppEventCenter instance of EventCenterForMicroApp
3295
3311
  */
3296
3312
  function recordDataCenterSnapshot(microAppEventCenter) {
3297
- if (microAppEventCenter && !microAppEventCenter.umdDataListeners) {
3298
- microAppEventCenter.umdDataListeners = { global: new Set(), normal: new Set() };
3313
+ var _a, _b;
3314
+ if (microAppEventCenter) {
3315
+ microAppEventCenter.umdDataListeners = {
3316
+ global: new Set((_a = microAppEventCenter.umdDataListeners) === null || _a === void 0 ? void 0 : _a.global),
3317
+ normal: new Set((_b = microAppEventCenter.umdDataListeners) === null || _b === void 0 ? void 0 : _b.normal),
3318
+ };
3299
3319
  const globalEventInfo = eventCenter.eventList.get('global');
3300
3320
  if (globalEventInfo) {
3301
3321
  for (const cb of globalEventInfo.callbacks) {
@@ -3306,7 +3326,9 @@ function recordDataCenterSnapshot(microAppEventCenter) {
3306
3326
  }
3307
3327
  const subAppEventInfo = eventCenter.eventList.get(createEventName(microAppEventCenter.appName, true));
3308
3328
  if (subAppEventInfo) {
3309
- microAppEventCenter.umdDataListeners.normal = new Set(subAppEventInfo.callbacks);
3329
+ for (const cb of subAppEventInfo.callbacks) {
3330
+ microAppEventCenter.umdDataListeners.normal.add(cb);
3331
+ }
3310
3332
  }
3311
3333
  }
3312
3334
  }
@@ -3331,7 +3353,7 @@ function rebuildDataCenterSnapshot(microAppEventCenter) {
3331
3353
  * @param microAppEventCenter instance of EventCenterForMicroApp
3332
3354
  */
3333
3355
  function resetDataCenterSnapshot(microAppEventCenter) {
3334
- delete microAppEventCenter.umdDataListeners;
3356
+ microAppEventCenter === null || microAppEventCenter === void 0 ? true : delete microAppEventCenter.umdDataListeners;
3335
3357
  }
3336
3358
 
3337
3359
  // 管理 app 的单例
@@ -3473,19 +3495,23 @@ function createProxyDocument(appName, sandbox) {
3473
3495
  * 3. prerender/keep-alive(default, umd): not clear timers, record & rebuild events
3474
3496
  */
3475
3497
  const record = () => {
3476
- // record onclick handler
3477
- sstOnClickHandler = sstOnClickHandler || onClickHandler;
3498
+ /**
3499
+ * record onclick handler
3500
+ * onClickHandler maybe set again after prerender/keep-alive app hidden
3501
+ */
3502
+ sstOnClickHandler = onClickHandler || sstOnClickHandler;
3478
3503
  // record document event
3479
3504
  eventListenerMap.forEach((listenerList, type) => {
3480
3505
  if (listenerList.size) {
3481
- sstEventListenerMap.set(type, new Set(listenerList));
3506
+ const cacheList = sstEventListenerMap.get(type) || [];
3507
+ sstEventListenerMap.set(type, new Set([...cacheList, ...listenerList]));
3482
3508
  }
3483
3509
  });
3484
3510
  };
3485
3511
  // rebuild event and timer before remount app
3486
3512
  const rebuild = () => {
3487
3513
  // rebuild onclick event
3488
- if (sstOnClickHandler)
3514
+ if (sstOnClickHandler && !onClickHandler)
3489
3515
  proxyDocument.onclick = sstOnClickHandler;
3490
3516
  // rebuild document event
3491
3517
  sstEventListenerMap.forEach((listenerList, type) => {
@@ -3500,8 +3526,8 @@ function createProxyDocument(appName, sandbox) {
3500
3526
  // Clear the function bound by micro app through document.onclick
3501
3527
  if (isFunction(onClickHandler)) {
3502
3528
  rawRemoveEventListener.call(rawDocument, 'click', onClickHandler);
3503
- onClickHandler = null;
3504
3529
  }
3530
+ onClickHandler = null;
3505
3531
  // Clear document binding event
3506
3532
  if (eventListenerMap.size) {
3507
3533
  eventListenerMap.forEach((listenerList, type) => {
@@ -3631,7 +3657,8 @@ function patchWindowEffect(appName, microAppWindow) {
3631
3657
  // record window event
3632
3658
  eventListenerMap.forEach((listenerList, type) => {
3633
3659
  if (listenerList.size) {
3634
- sstEventListenerMap.set(type, new Set(listenerList));
3660
+ const cacheList = sstEventListenerMap.get(type) || [];
3661
+ sstEventListenerMap.set(type, new Set([...cacheList, ...listenerList]));
3635
3662
  }
3636
3663
  });
3637
3664
  };
@@ -3678,31 +3705,41 @@ function patchWindowEffect(appName, microAppWindow) {
3678
3705
 
3679
3706
  // set micro app state to origin state
3680
3707
  function setMicroState(appName, microState) {
3681
- const rawState = globalEnv.rawWindow.history.state;
3682
- const additionalState = {
3683
- microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
3684
- [appName]: microState
3685
- })
3686
- };
3687
- // create new state object
3688
- return assign({}, rawState, additionalState);
3708
+ if (isMemoryRouterEnabled(appName)) {
3709
+ const rawState = globalEnv.rawWindow.history.state;
3710
+ const additionalState = {
3711
+ microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
3712
+ [appName]: microState
3713
+ })
3714
+ };
3715
+ // create new state object
3716
+ return assign({}, rawState, additionalState);
3717
+ }
3718
+ return microState;
3689
3719
  }
3690
3720
  // delete micro app state form origin state
3691
3721
  function removeMicroState(appName, rawState) {
3692
- if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
3693
- if (!isUndefined(rawState.microAppState[appName])) {
3694
- delete rawState.microAppState[appName];
3695
- }
3696
- if (!Object.keys(rawState.microAppState).length) {
3697
- delete rawState.microAppState;
3722
+ if (isMemoryRouterEnabled(appName)) {
3723
+ if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
3724
+ if (!isUndefined(rawState.microAppState[appName])) {
3725
+ delete rawState.microAppState[appName];
3726
+ }
3727
+ if (!Object.keys(rawState.microAppState).length) {
3728
+ delete rawState.microAppState;
3729
+ }
3698
3730
  }
3731
+ return assign({}, rawState);
3699
3732
  }
3700
- return assign({}, rawState);
3733
+ return rawState;
3701
3734
  }
3702
3735
  // get micro app state form origin state
3703
3736
  function getMicroState(appName) {
3704
- var _a, _b;
3705
- return ((_b = (_a = globalEnv.rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.microAppState) === null || _b === void 0 ? void 0 : _b[appName]) || null;
3737
+ var _a;
3738
+ const rawState = globalEnv.rawWindow.history.state;
3739
+ if (isMemoryRouterEnabled(appName)) {
3740
+ return ((_a = rawState === null || rawState === void 0 ? void 0 : rawState.microAppState) === null || _a === void 0 ? void 0 : _a[appName]) || null;
3741
+ }
3742
+ return rawState;
3706
3743
  }
3707
3744
  const ENC_AD_RE = /&/g; // %M1
3708
3745
  const ENC_EQ_RE = /=/g; // %M2
@@ -3740,53 +3777,61 @@ function formatQueryAppName(appName) {
3740
3777
  function getMicroPathFromURL(appName) {
3741
3778
  var _a, _b;
3742
3779
  const rawLocation = globalEnv.rawWindow.location;
3743
- const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
3744
- const microPath = ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) || ((_b = queryObject.searchQuery) === null || _b === void 0 ? void 0 : _b[formatQueryAppName(appName)]);
3745
- return isString(microPath) ? decodeMicroPath(microPath) : null;
3780
+ if (isMemoryRouterEnabled(appName)) {
3781
+ const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
3782
+ const microPath = ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) || ((_b = queryObject.searchQuery) === null || _b === void 0 ? void 0 : _b[formatQueryAppName(appName)]);
3783
+ return isString(microPath) ? decodeMicroPath(microPath) : null;
3784
+ }
3785
+ return rawLocation.pathname + rawLocation.search + rawLocation.hash;
3746
3786
  }
3747
3787
  /**
3748
3788
  * Attach child app fullPath to browser url
3749
3789
  * @param appName app.name
3750
- * @param microLocation location of child app
3790
+ * @param targetLocation location of child app or rawLocation of window
3751
3791
  */
3752
- function setMicroPathToURL(appName, microLocation) {
3753
- let { pathname, search, hash } = globalEnv.rawWindow.location;
3754
- const queryObject = getQueryObjectFromURL(search, hash);
3755
- const encodedMicroPath = encodeMicroPath(microLocation.pathname +
3756
- microLocation.search +
3757
- microLocation.hash);
3758
- /**
3759
- * Is parent is hash router
3760
- * In fact, this is not true. It just means that the parameter is added to the hash
3761
- */
3792
+ function setMicroPathToURL(appName, targetLocation) {
3793
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3762
3794
  let isAttach2Hash = false;
3763
- // If hash exists and search does not exist, it is considered as a hash route
3764
- if (hash && !search) {
3765
- isAttach2Hash = true;
3766
- if (queryObject.hashQuery) {
3767
- queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
3768
- }
3769
- else {
3770
- queryObject.hashQuery = {
3771
- [formatQueryAppName(appName)]: encodedMicroPath
3772
- };
3773
- }
3774
- const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?';
3775
- hash = baseHash + stringifyQuery(queryObject.hashQuery);
3776
- }
3777
- else {
3778
- if (queryObject.searchQuery) {
3779
- queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath;
3795
+ if (isMemoryRouterEnabled(appName)) {
3796
+ let { pathname, search, hash } = globalEnv.rawWindow.location;
3797
+ const queryObject = getQueryObjectFromURL(search, hash);
3798
+ const encodedMicroPath = encodeMicroPath(targetFullPath);
3799
+ /**
3800
+ * Is parent is hash router
3801
+ * In fact, this is not true. It just means that the parameter is added to the hash
3802
+ */
3803
+ // If hash exists and search does not exist, it is considered as a hash route
3804
+ if (hash && !search) {
3805
+ isAttach2Hash = true;
3806
+ if (queryObject.hashQuery) {
3807
+ queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
3808
+ }
3809
+ else {
3810
+ queryObject.hashQuery = {
3811
+ [formatQueryAppName(appName)]: encodedMicroPath
3812
+ };
3813
+ }
3814
+ const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?';
3815
+ hash = baseHash + stringifyQuery(queryObject.hashQuery);
3780
3816
  }
3781
3817
  else {
3782
- queryObject.searchQuery = {
3783
- [formatQueryAppName(appName)]: encodedMicroPath
3784
- };
3818
+ if (queryObject.searchQuery) {
3819
+ queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath;
3820
+ }
3821
+ else {
3822
+ queryObject.searchQuery = {
3823
+ [formatQueryAppName(appName)]: encodedMicroPath
3824
+ };
3825
+ }
3826
+ search = '?' + stringifyQuery(queryObject.searchQuery);
3785
3827
  }
3786
- search = '?' + stringifyQuery(queryObject.searchQuery);
3828
+ return {
3829
+ fullPath: pathname + search + hash,
3830
+ isAttach2Hash,
3831
+ };
3787
3832
  }
3788
3833
  return {
3789
- fullPath: pathname + search + hash,
3834
+ fullPath: targetFullPath,
3790
3835
  isAttach2Hash,
3791
3836
  };
3792
3837
  }
@@ -3798,18 +3843,20 @@ function setMicroPathToURL(appName, microLocation) {
3798
3843
  function removeMicroPathFromURL(appName, targetLocation) {
3799
3844
  var _a, _b, _c, _d;
3800
3845
  let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
3801
- const queryObject = getQueryObjectFromURL(search, hash);
3802
3846
  let isAttach2Hash = false;
3803
- if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
3804
- isAttach2Hash = true;
3805
- (_b = queryObject.hashQuery) === null || _b === void 0 ? true : delete _b[formatQueryAppName(appName)];
3806
- const hashQueryStr = stringifyQuery(queryObject.hashQuery);
3807
- hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr;
3808
- }
3809
- else if ((_c = queryObject.searchQuery) === null || _c === void 0 ? void 0 : _c[formatQueryAppName(appName)]) {
3810
- (_d = queryObject.searchQuery) === null || _d === void 0 ? true : delete _d[formatQueryAppName(appName)];
3811
- const searchQueryStr = stringifyQuery(queryObject.searchQuery);
3812
- search = searchQueryStr ? '?' + searchQueryStr : '';
3847
+ if (isMemoryRouterEnabled(appName)) {
3848
+ const queryObject = getQueryObjectFromURL(search, hash);
3849
+ if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
3850
+ isAttach2Hash = true;
3851
+ (_b = queryObject.hashQuery) === null || _b === void 0 ? true : delete _b[formatQueryAppName(appName)];
3852
+ const hashQueryStr = stringifyQuery(queryObject.hashQuery);
3853
+ hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr;
3854
+ }
3855
+ else if ((_c = queryObject.searchQuery) === null || _c === void 0 ? void 0 : _c[formatQueryAppName(appName)]) {
3856
+ (_d = queryObject.searchQuery) === null || _d === void 0 ? true : delete _d[formatQueryAppName(appName)];
3857
+ const searchQueryStr = stringifyQuery(queryObject.searchQuery);
3858
+ search = searchQueryStr ? '?' + searchQueryStr : '';
3859
+ }
3813
3860
  }
3814
3861
  return {
3815
3862
  fullPath: pathname + search + hash,
@@ -3849,8 +3896,24 @@ function getNoHashMicroPathFromURL(appName, baseUrl) {
3849
3896
  */
3850
3897
  function isEffectiveApp(appName) {
3851
3898
  const app = appInstanceMap.get(appName);
3899
+ /**
3900
+ * !!(app && !app.isPrefetch && !app.isHidden())
3901
+ * 隐藏的keep-alive应用暂时不作为无效应用,原因如下
3902
+ * 1、隐藏后才执行去除浏览器上的微应用的路由信息的操作,导致微应用的路由信息无法去除
3903
+ * 2、如果保持隐藏应用内部正常跳转,阻止同步路由信息到浏览器,这样理论上是好的,但是对于location跳转改如何处理?location跳转是基于修改浏览器地址后发送popstate事件实现的,所以应该是在隐藏后不支持通过location进行跳转
3904
+ */
3852
3905
  return !!(app && !app.isPrefetch);
3853
3906
  }
3907
+ /**
3908
+ * Determine whether the app has enabled memory-router
3909
+ * NOTE:
3910
+ * 1. if sandbox disabled, memory-router is disabled
3911
+ * 2. if app not exist, memory-router is disabled
3912
+ */
3913
+ function isMemoryRouterEnabled(appName) {
3914
+ const app = appInstanceMap.get(appName);
3915
+ return !!(app && app.sandBox && app.useMemoryRouter);
3916
+ }
3854
3917
 
3855
3918
  /**
3856
3919
  * dispatch PopStateEvent & HashChangeEvent to child app
@@ -3872,26 +3935,7 @@ function addHistoryListener(appName) {
3872
3935
  excludePreRender: true,
3873
3936
  }).includes(appName) &&
3874
3937
  !e.onlyForBrowser) {
3875
- const microPath = getMicroPathFromURL(appName);
3876
- const app = appInstanceMap.get(appName);
3877
- const proxyWindow = app.sandBox.proxyWindow;
3878
- const microAppWindow = app.sandBox.microAppWindow;
3879
- let isHashChange = false;
3880
- // for hashChangeEvent
3881
- const oldHref = proxyWindow.location.href;
3882
- // Do not attach micro state to url when microPath is empty
3883
- if (microPath) {
3884
- const oldHash = proxyWindow.location.hash;
3885
- updateMicroLocation(appName, microPath, microAppWindow.location);
3886
- isHashChange = proxyWindow.location.hash !== oldHash;
3887
- }
3888
- // dispatch formatted popStateEvent to child
3889
- dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow);
3890
- // dispatch formatted hashChangeEvent to child when hash change
3891
- if (isHashChange)
3892
- dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref);
3893
- // clear element scope before trigger event of next app
3894
- removeDomScope();
3938
+ updateMicroLocationWithEvent(appName, getMicroPathFromURL(appName));
3895
3939
  }
3896
3940
  };
3897
3941
  rawWindow.addEventListener('popstate', popStateHandler);
@@ -3899,6 +3943,35 @@ function addHistoryListener(appName) {
3899
3943
  rawWindow.removeEventListener('popstate', popStateHandler);
3900
3944
  };
3901
3945
  }
3946
+ /**
3947
+ * Effect: use to trigger child app jump
3948
+ * Actions:
3949
+ * 1. update microLocation with target path
3950
+ * 2. dispatch popStateEvent & hashChangeEvent
3951
+ * @param appName app name
3952
+ * @param targetFullPath target path of child app
3953
+ */
3954
+ function updateMicroLocationWithEvent(appName, targetFullPath) {
3955
+ const app = appInstanceMap.get(appName);
3956
+ const proxyWindow = app.sandBox.proxyWindow;
3957
+ const microAppWindow = app.sandBox.microAppWindow;
3958
+ let isHashChange = false;
3959
+ // for hashChangeEvent
3960
+ const oldHref = proxyWindow.location.href;
3961
+ // Do not attach micro state to url when targetFullPath is empty
3962
+ if (targetFullPath) {
3963
+ const oldHash = proxyWindow.location.hash;
3964
+ updateMicroLocation(appName, targetFullPath, microAppWindow.location);
3965
+ isHashChange = proxyWindow.location.hash !== oldHash;
3966
+ }
3967
+ // dispatch formatted popStateEvent to child
3968
+ dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow);
3969
+ // dispatch formatted hashChangeEvent to child when hash change
3970
+ if (isHashChange)
3971
+ dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref);
3972
+ // clear element scope before trigger event of next app
3973
+ removeDomScope();
3974
+ }
3902
3975
  /**
3903
3976
  * dispatch formatted popstate event to microApp
3904
3977
  * @param appName app name
@@ -4061,6 +4134,8 @@ function nativeHistoryNavigate(appName, methodName, fullPath, state = null, titl
4061
4134
  * 2. proxyHistory.pushState/replaceState with limited popstateEvent
4062
4135
  * 3. api microApp.router.push/replace
4063
4136
  * 4. proxyLocation.hash = xxx
4137
+ * NOTE:
4138
+ * 1. hidden keep-alive app can jump internally, but will not synchronize to browser
4064
4139
  * @param appName app.name
4065
4140
  * @param methodName pushState/replaceState
4066
4141
  * @param result result of add/remove microApp path on browser url
@@ -4076,8 +4151,10 @@ function navigateWithNativeEvent(appName, methodName, result, onlyForBrowser, st
4076
4151
  const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
4077
4152
  // navigate with native history method
4078
4153
  nativeHistoryNavigate(appName, methodName, result.fullPath, state, title);
4079
- if (oldFullPath !== result.fullPath)
4154
+ // TODO: 如果所有模式统一发送popstate事件,则isMemoryRouterEnabled(appName)要去掉
4155
+ if (oldFullPath !== result.fullPath && isMemoryRouterEnabled(appName)) {
4080
4156
  dispatchNativeEvent(appName, onlyForBrowser, oldHref);
4157
+ }
4081
4158
  }
4082
4159
  }
4083
4160
  /**
@@ -4122,8 +4199,8 @@ function reWriteHistoryMethod(method) {
4122
4199
  excludeHiddenApp: true,
4123
4200
  excludePreRender: true,
4124
4201
  }).forEach(appName => {
4125
- const app = appInstanceMap.get(appName);
4126
- if (app.sandBox && app.useMemoryRouter && !getMicroPathFromURL(appName)) {
4202
+ if (isMemoryRouterEnabled(appName) && !getMicroPathFromURL(appName)) {
4203
+ const app = appInstanceMap.get(appName);
4127
4204
  attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
4128
4205
  }
4129
4206
  });
@@ -4172,12 +4249,14 @@ function createRouterApi() {
4172
4249
  return function (to) {
4173
4250
  const appName = formatAppName(to.name);
4174
4251
  if (appName && isString(to.path)) {
4175
- const app = appInstanceMap.get(appName);
4176
- if (app && (!app.sandBox || !app.useMemoryRouter)) {
4177
- return logError(`navigation failed, memory router of app ${appName} is closed`);
4178
- }
4179
- // active apps, include hidden keep-alive app
4180
- if (getActiveApps({ excludePreRender: true }).includes(appName)) {
4252
+ /**
4253
+ * active apps, exclude prerender app or hidden keep-alive app
4254
+ * NOTE:
4255
+ * 1. prerender app or hidden keep-alive app clear and record popstate event, so we cannot control app jump through the API
4256
+ * 2. disable memory-router
4257
+ */
4258
+ if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName)) {
4259
+ const app = appInstanceMap.get(appName);
4181
4260
  const microLocation = app.sandBox.proxyWindow.location;
4182
4261
  const targetLocation = createURL(to.path, microLocation.href);
4183
4262
  // Only get path data, even if the origin is different from microApp
@@ -4186,20 +4265,48 @@ function createRouterApi() {
4186
4265
  if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
4187
4266
  const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
4188
4267
  navigateWithRawHistory(appName, methodName, targetLocation, to.state);
4268
+ /**
4269
+ * TODO:
4270
+ * 1. 关闭虚拟路由的跳转地址不同:baseRoute + 子应用地址,文档中要说明
4271
+ * 2. 关闭虚拟路由时跳转方式不同:1、基座跳转但不发送popstate事件 2、控制子应用更新location,内部发送popstate事件。
4272
+ * 补充:
4273
+ * 核心思路:减小对基座的影响(就是子应用跳转不向基座发送popstate事件,其他操作一致),但这是必要的吗,只是多了一个触发popstate的操作
4274
+ * 未来的思路有两种:
4275
+ * 1、减少对基座的影响,主要是解决vue循环刷新的问题
4276
+ * 2、全局发送popstate事件,解决主、子都是vue3的冲突问题
4277
+ * 两者选一个吧,如果选2,则下面这两行代码可以去掉
4278
+ * 要不这样吧,history和search模式采用2,这样可以解决vue3的问题,custom采用1,避免vue循环刷新的问题,这样在用户出现问题时各有解决方案。但反过来说,每种方案又分别导致另外的问题,不统一,导致复杂度增高
4279
+ * 如果关闭虚拟路由,同时发送popstate事件还是无法解决vue3的问题(毕竟history.state理论上还是会冲突),那么就没必要发送popstate事件了。
4280
+ * 。。。。先这样吧
4281
+ */
4282
+ if (!isMemoryRouterEnabled(appName)) {
4283
+ updateMicroLocationWithEvent(appName, targetFullPath);
4284
+ }
4189
4285
  }
4190
4286
  }
4191
4287
  else {
4192
- /**
4193
- * app not exit or unmounted, update browser URL with replaceState
4194
- * use base app location.origin as baseURL
4195
- */
4196
- const rawLocation = globalEnv.rawWindow.location;
4197
- const targetLocation = createURL(to.path, rawLocation.origin);
4198
- const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
4199
- if (getMicroPathFromURL(appName) !== targetFullPath) {
4200
- navigateWithRawHistory(appName, to.replace === false ? 'pushState' : 'replaceState', targetLocation, to.state);
4201
- }
4288
+ logWarn('navigation failed, app does not exist or is inactive');
4202
4289
  }
4290
+ // /**
4291
+ // * app not exit or unmounted, update browser URL with replaceState
4292
+ // * use base app location.origin as baseURL
4293
+ // * 应用不存在或已卸载,依然使用replaceState来更新浏览器地址 -- 不合理
4294
+ // */
4295
+ // /**
4296
+ // * TODO: 应用还没渲染或已经卸载最好不要支持跳转了,我知道这是因为解决一些特殊场景,但这么做是非常反直觉的
4297
+ // * 并且在新版本中有多种路由模式,如果应用不存在,我们根本无法知道是哪种模式,那么这里的操作就无意义了。
4298
+ // */
4299
+ // const rawLocation = globalEnv.rawWindow.location
4300
+ // const targetLocation = createURL(to.path, rawLocation.origin)
4301
+ // const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash
4302
+ // if (getMicroPathFromURL(appName) !== targetFullPath) {
4303
+ // navigateWithRawHistory(
4304
+ // appName,
4305
+ // to.replace === false ? 'pushState' : 'replaceState',
4306
+ // targetLocation,
4307
+ // to.state,
4308
+ // )
4309
+ // }
4203
4310
  }
4204
4311
  else {
4205
4312
  logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`);
@@ -4259,8 +4366,8 @@ function createRouterApi() {
4259
4366
  * 2. useMemoryRouter is false
4260
4367
  */
4261
4368
  function commonHandlerForAttachToURL(appName) {
4262
- const app = appInstanceMap.get(appName);
4263
- if (app.sandBox && app.useMemoryRouter) {
4369
+ if (isMemoryRouterEnabled(appName)) {
4370
+ const app = appInstanceMap.get(appName);
4264
4371
  attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
4265
4372
  }
4266
4373
  }
@@ -4512,37 +4619,40 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4512
4619
  // Even if the origin is the same, developers still have the possibility of want to jump to a new page
4513
4620
  if (targetLocation.origin === proxyLocation.origin) {
4514
4621
  const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
4515
- /**
4516
- * change hash with location.href will not trigger the browser reload
4517
- * so we use pushState & reload to imitate href behavior
4518
- * NOTE:
4519
- * 1. if child app only change hash, it should not trigger browser reload
4520
- * 2. if address is same and has hash, it should not add route stack
4521
- */
4522
- if (targetLocation.pathname === proxyLocation.pathname &&
4523
- targetLocation.search === proxyLocation.search) {
4524
- let oldHref = null;
4525
- if (targetLocation.hash !== proxyLocation.hash) {
4526
- if (setMicroPathResult.isAttach2Hash)
4527
- oldHref = rawLocation.href;
4528
- nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4529
- }
4530
- if (targetLocation.hash) {
4531
- dispatchNativeEvent(appName, false, oldHref);
4622
+ // if disable memory-router, navigate directly through rawLocation
4623
+ if (isMemoryRouterEnabled(appName)) {
4624
+ /**
4625
+ * change hash with location.href will not trigger the browser reload
4626
+ * so we use pushState & reload to imitate href behavior
4627
+ * NOTE:
4628
+ * 1. if child app only change hash, it should not trigger browser reload
4629
+ * 2. if address is same and has hash, it should not add route stack
4630
+ */
4631
+ if (targetLocation.pathname === proxyLocation.pathname &&
4632
+ targetLocation.search === proxyLocation.search) {
4633
+ let oldHref = null;
4634
+ if (targetLocation.hash !== proxyLocation.hash) {
4635
+ if (setMicroPathResult.isAttach2Hash)
4636
+ oldHref = rawLocation.href;
4637
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4638
+ }
4639
+ if (targetLocation.hash) {
4640
+ dispatchNativeEvent(appName, false, oldHref);
4641
+ }
4642
+ else {
4643
+ reload();
4644
+ }
4645
+ return void 0;
4646
+ /**
4647
+ * when baseApp is hash router, address change of child can not reload browser
4648
+ * so we imitate behavior of browser (reload) manually
4649
+ */
4532
4650
  }
4533
- else {
4651
+ else if (setMicroPathResult.isAttach2Hash) {
4652
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4534
4653
  reload();
4654
+ return void 0;
4535
4655
  }
4536
- return void 0;
4537
- /**
4538
- * when baseApp is hash router, address change of child can not reload browser
4539
- * so we imitate behavior of browser (reload) manually
4540
- */
4541
- }
4542
- else if (setMicroPathResult.isAttach2Hash) {
4543
- nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4544
- reload();
4545
- return void 0;
4546
4656
  }
4547
4657
  return setMicroPathResult.fullPath;
4548
4658
  }
@@ -4634,19 +4744,34 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4634
4744
  }
4635
4745
  }
4636
4746
  else if (key === 'pathname') {
4637
- const targetPath = ('/' + value).replace(/^\/+/, '/') + proxyLocation.search + proxyLocation.hash;
4638
- handleForPathNameAndSearch(targetPath, 'pathname');
4747
+ if (isMemoryRouterEnabled(appName)) {
4748
+ const targetPath = ('/' + value).replace(/^\/+/, '/') + proxyLocation.search + proxyLocation.hash;
4749
+ handleForPathNameAndSearch(targetPath, 'pathname');
4750
+ }
4751
+ else {
4752
+ rawLocation.pathname = value;
4753
+ }
4639
4754
  }
4640
4755
  else if (key === 'search') {
4641
- const targetPath = proxyLocation.pathname + ('?' + value).replace(/^\?+/, '?') + proxyLocation.hash;
4642
- handleForPathNameAndSearch(targetPath, 'search');
4756
+ if (isMemoryRouterEnabled(appName)) {
4757
+ const targetPath = proxyLocation.pathname + ('?' + value).replace(/^\?+/, '?') + proxyLocation.hash;
4758
+ handleForPathNameAndSearch(targetPath, 'search');
4759
+ }
4760
+ else {
4761
+ rawLocation.search = value;
4762
+ }
4643
4763
  }
4644
4764
  else if (key === 'hash') {
4645
- const targetPath = proxyLocation.pathname + proxyLocation.search + ('#' + value).replace(/^#+/, '#');
4646
- const targetLocation = createURL(targetPath, url);
4647
- // The same hash will not trigger popStateEvent
4648
- if (targetLocation.hash !== proxyLocation.hash) {
4649
- navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
4765
+ if (isMemoryRouterEnabled(appName)) {
4766
+ const targetPath = proxyLocation.pathname + proxyLocation.search + ('#' + value).replace(/^#+/, '#');
4767
+ const targetLocation = createURL(targetPath, url);
4768
+ // The same hash will not trigger popStateEvent
4769
+ if (targetLocation.hash !== proxyLocation.hash) {
4770
+ navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
4771
+ }
4772
+ }
4773
+ else {
4774
+ rawLocation.hash = value;
4650
4775
  }
4651
4776
  }
4652
4777
  else {
@@ -4691,6 +4816,7 @@ function updateMicroLocation(appName, path, microLocation, type) {
4691
4816
  var _a;
4692
4817
  // record old values of microLocation to `from`
4693
4818
  const from = createGuardLocation(appName, microLocation);
4819
+ // if is iframeSandbox, microLocation muse be rawLocation of iframe, not proxyLocation
4694
4820
  const newLocation = createURL(path, microLocation.href);
4695
4821
  if (isIframeSandbox(appName)) {
4696
4822
  const microAppWindow = appInstanceMap.get(appName).sandBox.microAppWindow;
@@ -4709,6 +4835,15 @@ function updateMicroLocation(appName, path, microLocation, type) {
4709
4835
  }
4710
4836
  }
4711
4837
 
4838
+ /**
4839
+ * TODO: 关于关闭虚拟路由系统的临时笔记
4840
+ * 1. with沙箱关闭虚拟路由最好和iframe保持一致
4841
+ * 2. default-page无法使用,但是用基座的地址可以实现一样的效果
4842
+ * 3. keep-router-state功能失效,因为始终为true
4843
+ * 4. 基座控制子应用跳转地址改变,正确的值为:baseRoute + 子应用地址,这要在文档中说明,否则很容易出错,确实也很难理解
4844
+ * 5. 是否需要发送popstate事件,为了减小对基座的影响,现在不发送
4845
+ * 6. 关闭后导致的vue3路由冲突问题需要在文档中明确指出(2处:在关闭虚拟路由系统的配置那里着重说明,在vue常见问题中说明)
4846
+ */
4712
4847
  /**
4713
4848
  * The router system has two operations: read and write
4714
4849
  * Read through location and write through history & location
@@ -4740,7 +4875,9 @@ function initRouteStateWithURL(appName, microLocation, defaultPage) {
4740
4875
  }
4741
4876
  /**
4742
4877
  * initialize browser information according to microLocation
4743
- * called on sandbox.start or reshow of keep-alive app
4878
+ * Scenes:
4879
+ * 1. sandbox.start
4880
+ * 2. reshow of keep-alive app
4744
4881
  */
4745
4882
  function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
4746
4883
  // update microLocation with defaultPage
@@ -4763,14 +4900,14 @@ function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
4763
4900
  const { pathname, search, hash } = createURL(url);
4764
4901
  updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
4765
4902
  }
4766
- removeStateAndPathFromBrowser(appName);
4903
+ removePathFromBrowser(appName);
4767
4904
  clearRouterWhenUnmount(appName);
4768
4905
  }
4769
4906
  /**
4770
4907
  * remove microState from history.state and remove microPath from browserURL
4771
4908
  * called on sandbox.stop or hidden of keep-alive app
4772
4909
  */
4773
- function removeStateAndPathFromBrowser(appName) {
4910
+ function removePathFromBrowser(appName) {
4774
4911
  attachRouteToBrowserURL(appName, removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
4775
4912
  }
4776
4913
 
@@ -4914,6 +5051,7 @@ class WithSandBox {
4914
5051
  if (this.active)
4915
5052
  return;
4916
5053
  this.active = true;
5054
+ // TODO: with沙箱关闭虚拟路由保持和iframe一致
4917
5055
  if (useMemoryRouter) {
4918
5056
  if (isUndefined(this.microAppWindow.location)) {
4919
5057
  this.setMicroAppRouter(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, this.microAppWindow);
@@ -4950,16 +5088,18 @@ class WithSandBox {
4950
5088
  * @param keepRouteState prevent reset route
4951
5089
  * @param destroy completely destroy, delete cache resources
4952
5090
  * @param clearData clear data from base app
5091
+ * @param useMemoryRouter use virtual router
4953
5092
  */
4954
- stop({ umdMode, keepRouteState, destroy, clearData, }) {
5093
+ stop({ umdMode, keepRouteState, destroy, clearData, useMemoryRouter, }) {
5094
+ var _a;
4955
5095
  if (!this.active)
4956
5096
  return;
4957
5097
  this.recordAndReleaseEffect({ umdMode, clearData, destroy }, !umdMode || destroy);
4958
- if (this.removeHistoryListener) {
5098
+ if (useMemoryRouter) {
4959
5099
  this.clearRouteState(keepRouteState);
4960
- // release listener of popstate
4961
- this.removeHistoryListener();
4962
5100
  }
5101
+ // release listener of popstate for child app
5102
+ (_a = this.removeHistoryListener) === null || _a === void 0 ? void 0 : _a.call(this);
4963
5103
  /**
4964
5104
  * NOTE:
4965
5105
  * 1. injectedKeys and escapeKeys must be placed at the back
@@ -5401,7 +5541,7 @@ class WithSandBox {
5401
5541
  updateBrowserURLWithLocation(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location);
5402
5542
  }
5403
5543
  removeRouteInfoForKeepAliveApp() {
5404
- removeStateAndPathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__);
5544
+ removePathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__);
5405
5545
  }
5406
5546
  /**
5407
5547
  * Format all html elements when init
@@ -5551,7 +5691,8 @@ function patchWindowEffect$1(microAppWindow) {
5551
5691
  // record window event
5552
5692
  eventListenerMap.forEach((listenerList, type) => {
5553
5693
  if (listenerList.size) {
5554
- sstEventListenerMap.set(type, new Set(listenerList));
5694
+ const cacheList = sstEventListenerMap.get(type) || [];
5695
+ sstEventListenerMap.set(type, new Set([...cacheList, ...listenerList]));
5555
5696
  }
5556
5697
  });
5557
5698
  };
@@ -5839,19 +5980,23 @@ function patchDocumentEffect(appName, microAppWindow) {
5839
5980
  * 3. after init prerender app
5840
5981
  */
5841
5982
  const record = () => {
5842
- // record onclick handler
5843
- sstOnClickHandler = sstOnClickHandler || onClickHandler;
5983
+ /**
5984
+ * record onclick handler
5985
+ * onClickHandler maybe set again after prerender/keep-alive app hidden
5986
+ */
5987
+ sstOnClickHandler = onClickHandler || sstOnClickHandler;
5844
5988
  // record document event
5845
5989
  eventListenerMap.forEach((listenerList, type) => {
5846
5990
  if (listenerList.size) {
5847
- sstEventListenerMap.set(type, new Set(listenerList));
5991
+ const cacheList = sstEventListenerMap.get(type) || [];
5992
+ sstEventListenerMap.set(type, new Set([...cacheList, ...listenerList]));
5848
5993
  }
5849
5994
  });
5850
5995
  };
5851
5996
  // rebuild event and timer before remount app
5852
5997
  const rebuild = () => {
5853
5998
  // rebuild onclick event
5854
- if (sstOnClickHandler)
5999
+ if (sstOnClickHandler && !onClickHandler)
5855
6000
  microDocument.onclick = sstOnClickHandler;
5856
6001
  sstEventListenerMap.forEach((listenerList, type) => {
5857
6002
  for (const listener of listenerList) {
@@ -5864,8 +6009,8 @@ function patchDocumentEffect(appName, microAppWindow) {
5864
6009
  // Clear the function bound by micro app through document.onclick
5865
6010
  if (isFunction(onClickHandler)) {
5866
6011
  rawRemoveEventListener.call(rawDocument, 'click', onClickHandler);
5867
- onClickHandler = null;
5868
6012
  }
6013
+ onClickHandler = null;
5869
6014
  // Clear document binding event
5870
6015
  if (eventListenerMap.size) {
5871
6016
  eventListenerMap.forEach((listenerList, type) => {
@@ -6174,14 +6319,29 @@ class IframeSandbox {
6174
6319
  if (this.active)
6175
6320
  return;
6176
6321
  this.active = true;
6177
- // TODO: 虚拟路由升级
6178
- // eslint-disable-next-line
6179
- if (useMemoryRouter || true) {
6180
- this.initRouteState(defaultPage);
6181
- // unique listener of popstate event for sub app
6182
- this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
6183
- }
6184
- else {
6322
+ /**
6323
+ * Sync router info to iframe when exec sandbox.start with disable or enable memory-router
6324
+ * e.g.:
6325
+ * vue-router@4.x get target path by remove the base section from rawLocation.pathname
6326
+ * code: window.location.pathname.slice(base.length) || '/'; (base is baseroute)
6327
+ * NOTE:
6328
+ * 1. iframe router and browser router are separated, we should update iframe router manually
6329
+ * 2. withSandbox location is browser location when disable memory-router, so no need to do anything
6330
+ */
6331
+ /**
6332
+ * TODO:
6333
+ * 做一些记录:
6334
+ * 1. iframe关闭虚拟路由系统后,default-page无法使用,推荐用户直接使用浏览器地址控制首页渲染
6335
+ * 补充:keep-router-state 也无法配置,因为keep-router-state一定为true。
6336
+ * 2. 导航拦截、current.route 可以正常使用
6337
+ * 3. 可以正常控制子应用跳转,方式还是自上而下(也可以是子应用内部跳转,这种方式更好一点,减小对基座的影响,不会导致vue的循环刷新)
6338
+ * 4. 关闭虚拟路由以后会对应 route-mode='custom' 模式,包括with沙箱也会这么做
6339
+ * 5. 关闭虚拟路由是指尽可能模式没有虚拟路由的情况,子应用直接获取浏览器location和history,控制浏览器跳转
6340
+ */
6341
+ this.initRouteState(defaultPage);
6342
+ // unique listener of popstate event for child app
6343
+ this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
6344
+ if (!useMemoryRouter) {
6185
6345
  this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
6186
6346
  }
6187
6347
  /**
@@ -6197,15 +6357,15 @@ class IframeSandbox {
6197
6357
  }
6198
6358
  if (++IframeSandbox.activeCount === 1) ;
6199
6359
  }
6200
- stop({ umdMode, keepRouteState, destroy, clearData, }) {
6360
+ stop({ umdMode, keepRouteState, destroy, clearData, useMemoryRouter, }) {
6361
+ var _a;
6201
6362
  if (!this.active)
6202
6363
  return;
6203
6364
  this.recordAndReleaseEffect({ clearData }, !umdMode || destroy);
6204
- if (this.removeHistoryListener) {
6205
- this.clearRouteState(keepRouteState);
6206
- // release listener of popstate
6207
- this.removeHistoryListener();
6208
- }
6365
+ // if keep-route-state is true or disable memory-router, preserve microLocation state
6366
+ this.clearRouteState(keepRouteState || !useMemoryRouter);
6367
+ // release listener of popstate for child app
6368
+ (_a = this.removeHistoryListener) === null || _a === void 0 ? void 0 : _a.call(this);
6209
6369
  if (!umdMode || destroy) {
6210
6370
  this.deleteIframeElement();
6211
6371
  this.escapeKeys.forEach((key) => {
@@ -6275,8 +6435,9 @@ class IframeSandbox {
6275
6435
  * 2. unmount prerender app manually
6276
6436
  */
6277
6437
  resetEffectSnapshot() {
6278
- this.windowEffect.reset();
6279
- this.documentEffect.reset();
6438
+ var _a, _b;
6439
+ (_a = this.windowEffect) === null || _a === void 0 ? void 0 : _a.reset();
6440
+ (_b = this.documentEffect) === null || _b === void 0 ? void 0 : _b.reset();
6280
6441
  resetDataCenterSnapshot(this.microAppWindow.microApp);
6281
6442
  }
6282
6443
  /**
@@ -6287,14 +6448,16 @@ class IframeSandbox {
6287
6448
  * 3. after init prerender app
6288
6449
  */
6289
6450
  recordEffectSnapshot() {
6290
- this.windowEffect.record();
6291
- this.documentEffect.record();
6451
+ var _a, _b;
6452
+ (_a = this.windowEffect) === null || _a === void 0 ? void 0 : _a.record();
6453
+ (_b = this.documentEffect) === null || _b === void 0 ? void 0 : _b.record();
6292
6454
  recordDataCenterSnapshot(this.microAppWindow.microApp);
6293
6455
  }
6294
6456
  // rebuild umd snapshot before remount umd app
6295
6457
  rebuildEffectSnapshot() {
6296
- this.windowEffect.rebuild();
6297
- this.documentEffect.rebuild();
6458
+ var _a, _b;
6459
+ (_a = this.windowEffect) === null || _a === void 0 ? void 0 : _a.rebuild();
6460
+ (_b = this.documentEffect) === null || _b === void 0 ? void 0 : _b.rebuild();
6298
6461
  rebuildDataCenterSnapshot(this.microAppWindow.microApp);
6299
6462
  }
6300
6463
  /**
@@ -6308,14 +6471,14 @@ class IframeSandbox {
6308
6471
  * @param keepAlive is keep-alive app
6309
6472
  */
6310
6473
  releaseGlobalEffect({ clearData = false }) {
6311
- var _a, _b, _c;
6312
- this.windowEffect.release();
6313
- this.documentEffect.release();
6314
- (_a = this.microAppWindow.microApp) === null || _a === void 0 ? void 0 : _a.clearDataListener();
6315
- (_b = this.microAppWindow.microApp) === null || _b === void 0 ? void 0 : _b.clearGlobalDataListener();
6474
+ var _a, _b, _c, _d, _e;
6475
+ (_a = this.windowEffect) === null || _a === void 0 ? void 0 : _a.release();
6476
+ (_b = this.documentEffect) === null || _b === void 0 ? void 0 : _b.release();
6477
+ (_c = this.microAppWindow.microApp) === null || _c === void 0 ? void 0 : _c.clearDataListener();
6478
+ (_d = this.microAppWindow.microApp) === null || _d === void 0 ? void 0 : _d.clearGlobalDataListener();
6316
6479
  if (clearData) {
6317
6480
  microApp.clearData(this.microAppWindow.__MICRO_APP_NAME__);
6318
- (_c = this.microAppWindow.microApp) === null || _c === void 0 ? void 0 : _c.clearData();
6481
+ (_e = this.microAppWindow.microApp) === null || _e === void 0 ? void 0 : _e.clearData();
6319
6482
  }
6320
6483
  }
6321
6484
  // set __MICRO_APP_PRE_RENDER__ state
@@ -6445,7 +6608,7 @@ class IframeSandbox {
6445
6608
  updateBrowserURLWithLocation(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location);
6446
6609
  }
6447
6610
  removeRouteInfoForKeepAliveApp() {
6448
- removeStateAndPathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__);
6611
+ removePathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__);
6449
6612
  }
6450
6613
  /**
6451
6614
  * Format all html elements when init
@@ -6570,9 +6733,14 @@ class CreateApp {
6570
6733
  var _a;
6571
6734
  if (++this.loadSourceLevel === 2) {
6572
6735
  this.source.html = html;
6573
- this.setAppState(appStates.LOADED);
6574
- if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
6736
+ if (!this.isPrefetch && !this.isUnmounted()) {
6575
6737
  getRootContainer(this.container).mount(this);
6738
+ // Abandonment plan
6739
+ // if (this.isHidden()) {
6740
+ // getRootContainer(this.container!).unmount()
6741
+ // } else if (!this.isUnmounted()) {
6742
+ // getRootContainer(this.container!).mount(this)
6743
+ // }
6576
6744
  }
6577
6745
  else if (this.isPrerender) {
6578
6746
  /**
@@ -6613,7 +6781,7 @@ class CreateApp {
6613
6781
  */
6614
6782
  onLoadError(e) {
6615
6783
  this.loadSourceLevel = -1;
6616
- if (appStates.UNMOUNT !== this.state) {
6784
+ if (!this.isUnmounted()) {
6617
6785
  this.onerror(e);
6618
6786
  this.setAppState(appStates.LOAD_FAILED);
6619
6787
  }
@@ -6667,7 +6835,7 @@ class CreateApp {
6667
6835
  */
6668
6836
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
6669
6837
  // current this.container is <div prerender='true'></div>
6670
- cloneContainer(this.container, container, false);
6838
+ cloneContainer(container, this.container, false);
6671
6839
  /**
6672
6840
  * set this.container to <micro-app></micro-app>
6673
6841
  * NOTE:
@@ -6698,7 +6866,7 @@ class CreateApp {
6698
6866
  }
6699
6867
  this.setAppState(appStates.MOUNTING);
6700
6868
  // TODO: 将所有cloneContainer中的'as Element'去掉,兼容shadowRoot的场景
6701
- cloneContainer(this.source.html, this.container, !this.umdMode);
6869
+ cloneContainer(this.container, this.source.html, !this.umdMode);
6702
6870
  (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
6703
6871
  umdMode: this.umdMode,
6704
6872
  baseroute,
@@ -6778,13 +6946,28 @@ class CreateApp {
6778
6946
  * dispatch mounted event when app run finished
6779
6947
  */
6780
6948
  dispatchMountedEvent() {
6781
- if (appStates.UNMOUNT !== this.state) {
6949
+ var _a;
6950
+ if (!this.isUnmounted()) {
6782
6951
  this.setAppState(appStates.MOUNTED);
6783
6952
  // call window.onmount of child app
6784
6953
  execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONMOUNT), this.name, microGlobalEvent.ONMOUNT, microApp.getData(this.name, true));
6785
6954
  // dispatch event mounted to parent
6786
6955
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
6956
+ /**
6957
+ * Hidden Keep-alive app during resource loading, render normally to ensure their liveliness (running in the background) characteristics.
6958
+ * Actions:
6959
+ * 1. Record & release all global events after mount
6960
+ */
6961
+ if (this.isHidden()) {
6962
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordAndReleaseEffect({ keepAlive: true });
6963
+ }
6787
6964
  }
6965
+ /**
6966
+ * TODO: 这里增加一个处理,如果渲染完成时已经卸载,则进行一些操作
6967
+ * 如果是默认模式:删除所有事件和定时器
6968
+ * 如果是umd模式:重新记录和清空事件
6969
+ * 补充:非必需,优先级低
6970
+ */
6788
6971
  }
6789
6972
  /**
6790
6973
  * unmount app
@@ -6853,7 +7036,7 @@ class CreateApp {
6853
7036
  actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb, }) {
6854
7037
  var _a;
6855
7038
  if (this.umdMode && this.container && !destroy) {
6856
- cloneContainer(this.container, this.source.html, false);
7039
+ cloneContainer(this.source.html, this.container, false);
6857
7040
  }
6858
7041
  /**
6859
7042
  * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
@@ -6866,6 +7049,7 @@ class CreateApp {
6866
7049
  keepRouteState: keepRouteState && !destroy,
6867
7050
  destroy,
6868
7051
  clearData: clearData || destroy,
7052
+ useMemoryRouter: this.useMemoryRouter,
6869
7053
  });
6870
7054
  // dispatch unmount event to base app
6871
7055
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
@@ -6909,7 +7093,6 @@ class CreateApp {
6909
7093
  // called after lifeCyclesEvent
6910
7094
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
6911
7095
  }
6912
- this.container = cloneContainer(this.container, pureCreateElement('div'), false);
6913
7096
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.recordAndReleaseEffect({ keepAlive: true });
6914
7097
  callback === null || callback === void 0 ? void 0 : callback();
6915
7098
  }
@@ -6924,7 +7107,13 @@ class CreateApp {
6924
7107
  // dispatch beforeShow event to base app
6925
7108
  dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
6926
7109
  this.setKeepAliveState(keepAliveStates.KEEP_ALIVE_SHOW);
6927
- this.container = cloneContainer(this.container, container, false);
7110
+ this.container = cloneContainer(container, this.container, false);
7111
+ /**
7112
+ * TODO:
7113
+ * 1. iframe沙箱在关闭虚拟路由系统时,重新展示时不更新浏览器地址,这样和with沙箱保持一致。
7114
+ * 但是iframe是可以做到重新展示时更新浏览器地址的,这里临时不支持,等待后续with沙箱也支持的时候再优化
7115
+ * 只需要去除 if (this.useMemoryRouter) 的判断即可
7116
+ */
6928
7117
  if (this.useMemoryRouter) {
6929
7118
  // called before lifeCyclesEvent
6930
7119
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.setRouteInfoForKeepAliveApp();
@@ -6975,10 +7164,18 @@ class CreateApp {
6975
7164
  getKeepAliveState() {
6976
7165
  return this.keepAliveState;
6977
7166
  }
7167
+ // is app unmounted
7168
+ isUnmounted() {
7169
+ return appStates.UNMOUNT === this.state;
7170
+ }
7171
+ // is app already hidden
7172
+ isHidden() {
7173
+ return keepAliveStates.KEEP_ALIVE_HIDDEN === this.keepAliveState;
7174
+ }
6978
7175
  // get umd library, if it not exist, return empty object
6979
7176
  getUmdLibraryHooks() {
6980
7177
  // after execScripts, the app maybe unmounted
6981
- if (appStates.UNMOUNT !== this.state && this.sandBox) {
7178
+ if (!this.isUnmounted() && this.sandBox) {
6982
7179
  const libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
6983
7180
  const proxyWindow = this.sandBox.proxyWindow;
6984
7181
  // compatible with pre versions
@@ -7041,16 +7238,14 @@ function defineElement(tagName) {
7041
7238
  * If oldApp exist & appName is different, determine whether oldApp is running
7042
7239
  */
7043
7240
  if (formatAttrName !== this.appName && oldApp) {
7044
- if (oldApp.getAppState() !== appStates.UNMOUNT &&
7045
- oldApp.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN &&
7046
- !oldApp.isPrefetch) {
7241
+ if (!oldApp.isUnmounted() && !oldApp.isHidden() && !oldApp.isPrefetch) {
7047
7242
  this.setAttribute('name', this.appName);
7048
7243
  return logError(`app name conflict, an app named ${formatAttrName} is running`);
7049
7244
  }
7050
7245
  }
7051
7246
  if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
7052
7247
  if (formatAttrName === this.appName) {
7053
- this.handleUnmount(true, () => {
7248
+ this.unmount(true, () => {
7054
7249
  this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
7055
7250
  });
7056
7251
  }
@@ -7059,7 +7254,7 @@ function defineElement(tagName) {
7059
7254
  this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
7060
7255
  }
7061
7256
  else {
7062
- this.handleUnmount(this.getDestroyCompatibleResult(), () => {
7257
+ this.unmount(false, () => {
7063
7258
  this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
7064
7259
  });
7065
7260
  }
@@ -7131,15 +7326,13 @@ function defineElement(tagName) {
7131
7326
  */
7132
7327
  handleDisconnected(destroy = false, callback) {
7133
7328
  const app = appInstanceMap.get(this.appName);
7134
- if (app &&
7135
- app.getAppState() !== appStates.UNMOUNT &&
7136
- app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
7329
+ if (app && !app.isUnmounted() && !app.isHidden()) {
7137
7330
  // keep-alive
7138
7331
  if (this.getKeepAliveModeResult() && !destroy) {
7139
7332
  this.handleHiddenKeepAliveApp(callback);
7140
7333
  }
7141
7334
  else {
7142
- this.handleUnmount(destroy || this.getDestroyCompatibleResult(), callback);
7335
+ this.unmount(destroy, callback);
7143
7336
  }
7144
7337
  }
7145
7338
  }
@@ -7199,16 +7392,16 @@ function defineElement(tagName) {
7199
7392
  * 2. 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
7200
7393
  * 3. When scopecss, useSandbox of prefetch app different from target app, delete prefetch app and create new one
7201
7394
  */
7202
- if (oldApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
7395
+ if (oldApp.isHidden() &&
7203
7396
  oldApp.url === this.appUrl) {
7204
7397
  this.handleShowKeepAliveApp(oldApp);
7205
7398
  }
7206
- else if (oldAppUrl === targetUrl && (oldApp.getAppState() === appStates.UNMOUNT ||
7399
+ else if (oldAppUrl === targetUrl && (oldApp.isUnmounted() ||
7207
7400
  (oldApp.isPrefetch &&
7208
7401
  this.sameCoreOptions(oldApp)))) {
7209
- this.handleAppMount(oldApp);
7402
+ this.handleMount(oldApp);
7210
7403
  }
7211
- else if (oldApp.isPrefetch || oldApp.getAppState() === appStates.UNMOUNT) {
7404
+ else if (oldApp.isPrefetch || oldApp.isUnmounted()) {
7212
7405
  if ((process.env.NODE_ENV !== 'production') && this.sameCoreOptions(oldApp)) {
7213
7406
  /**
7214
7407
  * url is different & old app is unmounted or prefetch, create new app to replace old one
@@ -7247,7 +7440,7 @@ function defineElement(tagName) {
7247
7440
  * scene5: if oldApp is KEEP_ALIVE_HIDDEN, name must different
7248
7441
  */
7249
7442
  if (oldApp) {
7250
- if (oldApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
7443
+ if (oldApp.isHidden()) {
7251
7444
  if (oldApp.url === this.appUrl) {
7252
7445
  this.handleShowKeepAliveApp(oldApp);
7253
7446
  }
@@ -7262,7 +7455,7 @@ function defineElement(tagName) {
7262
7455
  * 推荐:if (
7263
7456
  * oldApp.url === this.appUrl &&
7264
7457
  * oldApp.ssrUrl === this.ssrUrl && (
7265
- * oldApp.getAppState() === appStates.UNMOUNT ||
7458
+ * oldApp.isUnmounted() ||
7266
7459
  * (oldApp.isPrefetch && this.sameCoreOptions(oldApp))
7267
7460
  * )
7268
7461
  * )
@@ -7270,7 +7463,7 @@ function defineElement(tagName) {
7270
7463
  }
7271
7464
  else if (oldApp.url === this.appUrl && oldApp.ssrUrl === this.ssrUrl) {
7272
7465
  // mount app
7273
- this.handleAppMount(oldApp);
7466
+ this.handleMount(oldApp);
7274
7467
  }
7275
7468
  else {
7276
7469
  this.handleCreateApp();
@@ -7317,7 +7510,7 @@ function defineElement(tagName) {
7317
7510
  const oldApp = appInstanceMap.get(this.appName);
7318
7511
  if (oldApp) {
7319
7512
  if (oldApp.isPrerender) {
7320
- this.handleUnmount(true, createAppInstance);
7513
+ this.unmount(true, createAppInstance);
7321
7514
  }
7322
7515
  else {
7323
7516
  oldApp.actionsForCompletelyDestroy();
@@ -7335,7 +7528,7 @@ function defineElement(tagName) {
7335
7528
  * 2. is remount in another container ?
7336
7529
  * 3. is remount with change properties of the container ?
7337
7530
  */
7338
- handleAppMount(app) {
7531
+ handleMount(app) {
7339
7532
  app.isPrefetch = false;
7340
7533
  // TODO: Can defer be removed?
7341
7534
  defer(() => this.mount(app));
@@ -7358,13 +7551,13 @@ function defineElement(tagName) {
7358
7551
  /**
7359
7552
  * unmount app
7360
7553
  * @param destroy delete cache resources when unmount
7554
+ * @param unmountcb callback
7361
7555
  */
7362
- handleUnmount(destroy, unmountcb) {
7556
+ unmount(destroy, unmountcb) {
7363
7557
  const app = appInstanceMap.get(this.appName);
7364
- if (app &&
7365
- app.getAppState() !== appStates.UNMOUNT) {
7558
+ if (app && !app.isUnmounted()) {
7366
7559
  app.unmount({
7367
- destroy,
7560
+ destroy: destroy || this.getDestroyCompatibleResult(),
7368
7561
  clearData: this.getDisposeResult('clear-data'),
7369
7562
  keepRouteState: this.getDisposeResult('keep-router-state'),
7370
7563
  unmountcb,
@@ -7374,9 +7567,7 @@ function defineElement(tagName) {
7374
7567
  // hidden app when disconnectedCallback called with keep-alive
7375
7568
  handleHiddenKeepAliveApp(callback) {
7376
7569
  const app = appInstanceMap.get(this.appName);
7377
- if (app &&
7378
- app.getAppState() !== appStates.UNMOUNT &&
7379
- app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
7570
+ if (app && !app.isUnmounted() && !app.isHidden()) {
7380
7571
  app.hiddenKeepAliveApp(callback);
7381
7572
  }
7382
7573
  }
@@ -7674,10 +7865,10 @@ function fetchGlobalResources(resources, suffix, sourceHandler) {
7674
7865
  function getActiveApps({ excludeHiddenApp = false, excludePreRender = false, } = {}) {
7675
7866
  const activeApps = [];
7676
7867
  appInstanceMap.forEach((app, appName) => {
7677
- if (appStates.UNMOUNT !== app.getAppState() &&
7868
+ if (!app.isUnmounted() &&
7678
7869
  (!app.isPrefetch || (app.isPrerender && !excludePreRender)) &&
7679
7870
  (!excludeHiddenApp ||
7680
- keepAliveStates.KEEP_ALIVE_HIDDEN !== app.getKeepAliveState())) {
7871
+ !app.isHidden())) {
7681
7872
  activeApps.push(appName);
7682
7873
  }
7683
7874
  });
@@ -7697,7 +7888,7 @@ function unmountApp(appName, options) {
7697
7888
  const app = appInstanceMap.get(formatAppName(appName));
7698
7889
  return new Promise((resolve) => {
7699
7890
  if (app) {
7700
- if (app.getAppState() === appStates.UNMOUNT || app.isPrefetch) {
7891
+ if (app.isUnmounted() || app.isPrefetch) {
7701
7892
  if (app.isPrerender) {
7702
7893
  app.unmount({
7703
7894
  destroy: !!(options === null || options === void 0 ? void 0 : options.destroy),
@@ -7712,7 +7903,7 @@ function unmountApp(appName, options) {
7712
7903
  resolve(true);
7713
7904
  }
7714
7905
  }
7715
- else if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
7906
+ else if (app.isHidden()) {
7716
7907
  if (options === null || options === void 0 ? void 0 : options.destroy) {
7717
7908
  app.unmount({
7718
7909
  destroy: true,