@micro-zoe/micro-app 1.0.0-beta.6 → 1.0.0-rc.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
@@ -62,6 +62,7 @@ declare module '@micro-zoe/micro-app/micro_app' {
62
62
  export function renderApp(options: RenderAppOptions): Promise<boolean>;
63
63
  export class MicroApp extends EventCenterForBaseApp implements MicroAppBaseType {
64
64
  tagName: string;
65
+ hasInit: boolean;
65
66
  options: OptionsType;
66
67
  router: Router;
67
68
  preFetch: typeof preFetch;
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '1.0.0-beta.6';
1
+ const version = '1.0.0-rc.0';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -357,20 +357,23 @@ function pureCreateElement(tagName, options) {
357
357
  * @param deep deep clone or transfer dom
358
358
  */
359
359
  function cloneContainer(target, origin, deep) {
360
- target.innerHTML = '';
361
- if (deep) {
362
- // TODO: ShadowRoot兼容,ShadowRoot不能直接使用cloneNode
363
- const clonedNode = origin.cloneNode(true);
364
- const fragment = document.createDocumentFragment();
365
- Array.from(clonedNode.childNodes).forEach((node) => {
366
- fragment.appendChild(node);
367
- });
368
- target.appendChild(fragment);
369
- }
370
- else {
371
- Array.from(origin.childNodes).forEach((node) => {
372
- target.appendChild(node);
373
- });
360
+ // 在基座接受到afterhidden方法后立即执行unmount,彻底destroy应用时,因为unmount时同步执行,所以this.container为null后才执行cloneContainer
361
+ if (origin) {
362
+ target.innerHTML = '';
363
+ if (deep) {
364
+ // TODO: ShadowRoot兼容,ShadowRoot不能直接使用cloneNode
365
+ const clonedNode = origin.cloneNode(true);
366
+ const fragment = document.createDocumentFragment();
367
+ Array.from(clonedNode.childNodes).forEach((node) => {
368
+ fragment.appendChild(node);
369
+ });
370
+ target.appendChild(fragment);
371
+ }
372
+ else {
373
+ Array.from(origin.childNodes).forEach((node) => {
374
+ target.appendChild(node);
375
+ });
376
+ }
374
377
  }
375
378
  return target;
376
379
  }
@@ -606,10 +609,11 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
606
609
  * @param detail event detail
607
610
  */
608
611
  function dispatchCustomEventToMicroApp(app, eventName, detail = {}) {
612
+ var _a;
609
613
  const event = new CustomEvent(eventName, {
610
614
  detail,
611
615
  });
612
- app.sandBox.microAppWindow.dispatchEvent(event);
616
+ (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.microAppWindow.dispatchEvent(event);
613
617
  }
614
618
 
615
619
  /**
@@ -1394,6 +1398,7 @@ var appStates;
1394
1398
  appStates["CREATED"] = "created";
1395
1399
  appStates["LOADING"] = "loading";
1396
1400
  appStates["LOAD_FAILED"] = "load_failed";
1401
+ appStates["BEFORE_MOUNT"] = "before_mount";
1397
1402
  appStates["MOUNTING"] = "mounting";
1398
1403
  appStates["MOUNTED"] = "mounted";
1399
1404
  appStates["UNMOUNT"] = "unmount";
@@ -3509,7 +3514,7 @@ function isEffectiveApp(appName) {
3509
3514
  const app = appInstanceMap.get(appName);
3510
3515
  /**
3511
3516
  * !!(app && !app.isPrefetch && !app.isHidden())
3512
- * TODO: 隐藏的keep-alive应用暂时不作为无效应用,原因如下
3517
+ * NOTE: 隐藏的keep-alive应用暂时不作为无效应用,原因如下
3513
3518
  * 1、隐藏后才执行去除浏览器上的微应用的路由信息的操作,导致微应用的路由信息无法去除
3514
3519
  * 2、如果保持隐藏应用内部正常跳转,阻止同步路由信息到浏览器,这样理论上是好的,但是对于location跳转改如何处理?location跳转是基于修改浏览器地址后发送popstate事件实现的,所以应该是在隐藏后不支持通过location进行跳转
3515
3520
  */
@@ -3865,6 +3870,39 @@ function createRouterApi() {
3865
3870
  // clear element scope after navigate
3866
3871
  removeDomScope();
3867
3872
  }
3873
+ /**
3874
+ * navigation handler
3875
+ * @param appName app.name
3876
+ * @param app app instance
3877
+ * @param to router target options
3878
+ * @param replace use router.replace?
3879
+ */
3880
+ function handleNavigate(appName, app, to, replace) {
3881
+ const microLocation = app.sandBox.proxyWindow.location;
3882
+ const targetLocation = createURL(to.path, microLocation.href);
3883
+ // Only get path data, even if the origin is different from microApp
3884
+ const currentFullPath = microLocation.pathname + microLocation.search + microLocation.hash;
3885
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3886
+ if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
3887
+ const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3888
+ navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3889
+ /**
3890
+ * TODO:
3891
+ * 1. 关闭虚拟路由的跳转地址不同:baseRoute + 子应用地址,文档中要说明
3892
+ * 2. 关闭虚拟路由时跳转方式不同:1、基座跳转但不发送popstate事件 2、控制子应用更新location,内部发送popstate事件。
3893
+ * 核心思路:减小对基座的影响(子应用跳转不向基座发送popstate事件,其他操作一致),但这是必要的吗,只是多了一个触发popstate的操作
3894
+ * 路由优化方案有两种:
3895
+ * 1、减少对基座的影响,主要是解决vue循环刷新的问题
3896
+ * 2、全局发送popstate事件,解决主、子都是vue3的冲突问题
3897
+ * 两者选一个吧,如果选2,则下面这两行代码可以去掉
3898
+ * NOTE1: history和search模式采用2,这样可以解决vue3的问题,custom采用1,避免vue循环刷新的问题,这样在用户出现问题时各有解决方案。但反过来说,每种方案又分别导致另外的问题,不统一,导致复杂度增高
3899
+ * NOTE2: 关闭虚拟路由,同时发送popstate事件还是无法解决vue3的问题(毕竟history.state理论上还是会冲突),那么就没必要发送popstate事件了。
3900
+ */
3901
+ if (isRouterModeCustom(appName)) {
3902
+ updateMicroLocationWithEvent(appName, targetFullPath);
3903
+ }
3904
+ }
3905
+ }
3868
3906
  /**
3869
3907
  * create method of router.push/replace
3870
3908
  * NOTE:
@@ -3883,33 +3921,16 @@ function createRouterApi() {
3883
3921
  * 1. prerender app or hidden keep-alive app clear and record popstate event, so we cannot control app jump through the API
3884
3922
  * 2. disable memory-router
3885
3923
  */
3924
+ /**
3925
+ * TODO: 子应用开始渲染但是还没渲染完成
3926
+ * 1、调用跳转改如何处理
3927
+ * 2、iframe的沙箱还没初始化时执行跳转报错,如何处理。。。
3928
+ * 3、hidden app 是否支持跳转
3929
+ */
3886
3930
  if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName)) {
3887
3931
  const app = appInstanceMap.get(appName);
3888
- const microLocation = app.sandBox.proxyWindow.location;
3889
- const targetLocation = createURL(to.path, microLocation.href);
3890
- // Only get path data, even if the origin is different from microApp
3891
- const currentFullPath = microLocation.pathname + microLocation.search + microLocation.hash;
3892
- const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3893
- if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
3894
- const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3895
- navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3896
- /**
3897
- * TODO:
3898
- * 1. 关闭虚拟路由的跳转地址不同:baseRoute + 子应用地址,文档中要说明
3899
- * 2. 关闭虚拟路由时跳转方式不同:1、基座跳转但不发送popstate事件 2、控制子应用更新location,内部发送popstate事件。
3900
- * 补充:
3901
- * 核心思路:减小对基座的影响(就是子应用跳转不向基座发送popstate事件,其他操作一致),但这是必要的吗,只是多了一个触发popstate的操作
3902
- * 未来的思路有两种:
3903
- * 1、减少对基座的影响,主要是解决vue循环刷新的问题
3904
- * 2、全局发送popstate事件,解决主、子都是vue3的冲突问题
3905
- * 两者选一个吧,如果选2,则下面这两行代码可以去掉
3906
- * 要不这样吧,history和search模式采用2,这样可以解决vue3的问题,custom采用1,避免vue循环刷新的问题,这样在用户出现问题时各有解决方案。但反过来说,每种方案又分别导致另外的问题,不统一,导致复杂度增高
3907
- * 如果关闭虚拟路由,同时发送popstate事件还是无法解决vue3的问题(毕竟history.state理论上还是会冲突),那么就没必要发送popstate事件了。
3908
- */
3909
- if (isRouterModeCustom(appName)) {
3910
- updateMicroLocationWithEvent(appName, targetFullPath);
3911
- }
3912
- }
3932
+ const navigateAction = () => handleNavigate(appName, app, to, replace);
3933
+ app.iframe ? app.sandBox.sandboxReady.then(navigateAction) : navigateAction();
3913
3934
  }
3914
3935
  else {
3915
3936
  logWarn('navigation failed, app does not exist or is inactive');
@@ -4639,6 +4660,8 @@ class WithSandBox {
4639
4660
  this.adapter = new Adapter();
4640
4661
  // get scopeProperties and escapeProperties from plugins
4641
4662
  this.getSpecialProperties(appName);
4663
+ // create location, history for child app
4664
+ this.patchRouter(appName, url, this.microAppWindow);
4642
4665
  // patch window of child app
4643
4666
  this.windowEffect = patchWindow(appName, this.microAppWindow, this);
4644
4667
  // patch document of child app
@@ -4658,10 +4681,6 @@ class WithSandBox {
4658
4681
  return;
4659
4682
  this.active = true;
4660
4683
  /* --- memory router part --- start */
4661
- // create location, history for child app
4662
- if (isUndefined(this.microAppWindow.location)) {
4663
- this.setMicroAppRouter(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, this.microAppWindow);
4664
- }
4665
4684
  // update microLocation, attach route info to browser url
4666
4685
  this.initRouteState(defaultPage);
4667
4686
  // unique listener of popstate event for sub app
@@ -4744,6 +4763,8 @@ class WithSandBox {
4744
4763
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
4745
4764
  microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
4746
4765
  microAppWindow.__MICRO_APP_UMD_MODE__ = false;
4766
+ microAppWindow.__MICRO_APP_SANDBOX__ = this;
4767
+ microAppWindow.__MICRO_APP_SANDBOX_TYPE__ = 'with';
4747
4768
  microAppWindow.rawWindow = globalEnv.rawWindow;
4748
4769
  microAppWindow.rawDocument = globalEnv.rawDocument;
4749
4770
  microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
@@ -4983,7 +5004,7 @@ class WithSandBox {
4983
5004
  });
4984
5005
  }
4985
5006
  // set location & history for memory router
4986
- setMicroAppRouter(appName, url, microAppWindow) {
5007
+ patchRouter(appName, url, microAppWindow) {
4987
5008
  const { microLocation, microHistory } = createMicroRouter(appName, url);
4988
5009
  rawDefineProperties(microAppWindow, {
4989
5010
  location: {
@@ -5036,7 +5057,7 @@ class WithSandBox {
5036
5057
  }
5037
5058
  WithSandBox.activeCount = 0; // number of active sandbox
5038
5059
 
5039
- function patchRoute(appName, url, microAppWindow, browserHost) {
5060
+ function patchRouter(appName, url, microAppWindow, browserHost) {
5040
5061
  const childStaticLocation = new URL(url);
5041
5062
  const childHost = childStaticLocation.protocol + '//' + childStaticLocation.host;
5042
5063
  const childFullPath = childStaticLocation.pathname + childStaticLocation.search + childStaticLocation.hash;
@@ -5275,6 +5296,15 @@ function patchDocumentPrototype(appName, microAppWindow) {
5275
5296
  const rawMicroGetElementsByClassName = microRootDocument.prototype.getElementsByClassName;
5276
5297
  const rawMicroGetElementsByTagName = microRootDocument.prototype.getElementsByTagName;
5277
5298
  const rawMicroGetElementsByName = microRootDocument.prototype.getElementsByName;
5299
+ const rawMicroElementFromPoint = microRootDocument.prototype.elementFromPoint;
5300
+ const rawMicroCaretRangeFromPoint = microRootDocument.prototype.caretRangeFromPoint;
5301
+ microRootDocument.prototype.caretRangeFromPoint = function caretRangeFromPoint(x, y) {
5302
+ // 这里this指向document才可以获取到子应用的document实例,range才可以被成功生成
5303
+ const element = rawMicroElementFromPoint.call(rawDocument, x, y);
5304
+ const range = rawMicroCaretRangeFromPoint.call(rawDocument, x, y);
5305
+ updateElementInfo(element, appName);
5306
+ return range;
5307
+ };
5278
5308
  microRootDocument.prototype.createElement = function createElement(tagName, options) {
5279
5309
  const element = rawMicroCreateElement.call(this, tagName, options);
5280
5310
  return updateElementInfo(element, appName);
@@ -5790,13 +5820,12 @@ class IframeSandbox {
5790
5820
  this.deleteIframeElement = this.createIframeElement(appName, browserHost);
5791
5821
  this.microAppWindow = this.iframe.contentWindow;
5792
5822
  this.patchIframe(this.microAppWindow, (resolve) => {
5793
- // TODO: 优化代码
5794
5823
  // create new html to iframe
5795
5824
  this.createIframeTemplate(this.microAppWindow);
5796
5825
  // get escapeProperties from plugins
5797
5826
  this.getSpecialProperties(appName);
5798
5827
  // patch location & history of child app
5799
- this.proxyLocation = patchRoute(appName, url, this.microAppWindow, browserHost);
5828
+ this.proxyLocation = patchRouter(appName, url, this.microAppWindow, browserHost);
5800
5829
  // patch window of child app
5801
5830
  this.windowEffect = patchWindow$1(appName, this.microAppWindow, this);
5802
5831
  // patch document of child app
@@ -5807,9 +5836,9 @@ class IframeSandbox {
5807
5836
  * create static properties
5808
5837
  * NOTE:
5809
5838
  * 1. execute as early as possible
5810
- * 2. run after patchRoute & createProxyWindow
5839
+ * 2. run after patchRouter & createProxyWindow
5811
5840
  */
5812
- this.initStaticGlobalKeys(appName, url);
5841
+ this.initStaticGlobalKeys(appName, url, this.microAppWindow);
5813
5842
  resolve();
5814
5843
  });
5815
5844
  }
@@ -5914,22 +5943,23 @@ class IframeSandbox {
5914
5943
  * create static properties
5915
5944
  * NOTE:
5916
5945
  * 1. execute as early as possible
5917
- * 2. run after patchRoute & createProxyWindow
5946
+ * 2. run after patchRouter & createProxyWindow
5918
5947
  */
5919
- initStaticGlobalKeys(appName, url) {
5920
- this.microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
5921
- this.microAppWindow.__MICRO_APP_NAME__ = appName;
5922
- this.microAppWindow.__MICRO_APP_URL__ = url;
5923
- this.microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
5924
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = '';
5925
- this.microAppWindow.__MICRO_APP_WINDOW__ = this.microAppWindow;
5926
- this.microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
5927
- this.microAppWindow.__MICRO_APP_UMD_MODE__ = false;
5928
- this.microAppWindow.__MICRO_APP_SANDBOX__ = this;
5929
- this.microAppWindow.__MICRO_APP_PROXY_WINDOW__ = this.proxyWindow;
5930
- this.microAppWindow.rawWindow = globalEnv.rawWindow;
5931
- this.microAppWindow.rawDocument = globalEnv.rawDocument;
5932
- this.microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
5948
+ initStaticGlobalKeys(appName, url, microAppWindow) {
5949
+ microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
5950
+ microAppWindow.__MICRO_APP_NAME__ = appName;
5951
+ microAppWindow.__MICRO_APP_URL__ = url;
5952
+ microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
5953
+ microAppWindow.__MICRO_APP_BASE_ROUTE__ = '';
5954
+ microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
5955
+ microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
5956
+ microAppWindow.__MICRO_APP_UMD_MODE__ = false;
5957
+ microAppWindow.__MICRO_APP_PROXY_WINDOW__ = this.proxyWindow;
5958
+ microAppWindow.__MICRO_APP_SANDBOX__ = this;
5959
+ microAppWindow.__MICRO_APP_SANDBOX_TYPE__ = 'iframe';
5960
+ microAppWindow.rawWindow = globalEnv.rawWindow;
5961
+ microAppWindow.rawDocument = globalEnv.rawDocument;
5962
+ microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
5933
5963
  removeDomScope,
5934
5964
  pureCreateElement,
5935
5965
  location: this.proxyLocation,
@@ -6026,7 +6056,12 @@ class IframeSandbox {
6026
6056
  (function iframeLocationReady() {
6027
6057
  setTimeout(() => {
6028
6058
  try {
6029
- if (microAppWindow.document === oldMicroDocument) {
6059
+ /**
6060
+ * NOTE:
6061
+ * 1. In browser, iframe document will be recreated after iframe initial
6062
+ * 2. In jest, iframe document is always the same
6063
+ */
6064
+ if (microAppWindow.document === oldMicroDocument && !false) {
6030
6065
  iframeLocationReady();
6031
6066
  }
6032
6067
  else {
@@ -6123,7 +6158,7 @@ IframeSandbox.activeCount = 0; // number of active sandbox
6123
6158
  // micro app instances
6124
6159
  const appInstanceMap = new Map();
6125
6160
  class CreateApp {
6126
- constructor({ name, url, container, scopecss, useSandbox, inline, iframe, ssrUrl, isPrefetch, prefetchLevel, }) {
6161
+ constructor({ name, url, container, scopecss, useSandbox, inline, iframe, ssrUrl, isPrefetch, prefetchLevel, routerMode, }) {
6127
6162
  this.state = appStates.CREATED;
6128
6163
  this.keepAliveState = null;
6129
6164
  this.loadSourceLevel = 0;
@@ -6133,7 +6168,6 @@ class CreateApp {
6133
6168
  // TODO: 类型优化,加上iframe沙箱
6134
6169
  this.sandBox = null;
6135
6170
  this.fiber = false;
6136
- this.routerMode = DEFAULT_ROUTER_MODE;
6137
6171
  appInstanceMap.set(name, this);
6138
6172
  // init actions
6139
6173
  this.name = name;
@@ -6142,6 +6176,11 @@ class CreateApp {
6142
6176
  this.scopecss = this.useSandbox && scopecss;
6143
6177
  this.inline = inline !== null && inline !== void 0 ? inline : false;
6144
6178
  this.iframe = iframe !== null && iframe !== void 0 ? iframe : false;
6179
+ /**
6180
+ * NOTE:
6181
+ * 1. Navigate after micro-app created, before mount
6182
+ */
6183
+ this.routerMode = routerMode || DEFAULT_ROUTER_MODE;
6145
6184
  // not exist when prefetch 👇
6146
6185
  this.container = container !== null && container !== void 0 ? container : null;
6147
6186
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
@@ -6237,6 +6276,8 @@ class CreateApp {
6237
6276
  return this.setAppState(appStates.LOADING);
6238
6277
  }
6239
6278
  this.createSandbox();
6279
+ // place outside of nextAction, as nextAction may execute async
6280
+ this.setAppState(appStates.BEFORE_MOUNT);
6240
6281
  const nextAction = () => {
6241
6282
  var _a, _b, _c, _d, _e, _f, _g;
6242
6283
  /**
@@ -6317,6 +6358,11 @@ class CreateApp {
6317
6358
  this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6318
6359
  }
6319
6360
  catch (e) {
6361
+ /**
6362
+ * TODO:
6363
+ * 1. 是否应该直接抛出错误
6364
+ * 2. 是否应该触发error生命周期
6365
+ */
6320
6366
  logError('An error occurred in window.mount \n', this.name, e);
6321
6367
  }
6322
6368
  }
@@ -6350,7 +6396,10 @@ class CreateApp {
6350
6396
  if (isPromise(umdHookMountResult)) {
6351
6397
  umdHookMountResult
6352
6398
  .then(() => this.dispatchMountedEvent())
6353
- .catch(() => this.dispatchMountedEvent());
6399
+ .catch((e) => {
6400
+ logError('An error occurred in window.mount \n', this.name, e);
6401
+ this.dispatchMountedEvent();
6402
+ });
6354
6403
  }
6355
6404
  else {
6356
6405
  this.dispatchMountedEvent();
@@ -6502,8 +6551,12 @@ class CreateApp {
6502
6551
  var _a, _b;
6503
6552
  this.setKeepAliveState(keepAliveStates.KEEP_ALIVE_HIDDEN);
6504
6553
  /**
6505
- * event should dispatch before clone node
6506
- * dispatch afterHidden event to micro-app
6554
+ * afterhidden事件需要提前发送,原因如下:
6555
+ * 1. 此时发送this.container还指向micro-app元素,而不是临时div元素
6556
+ * 2. 沙箱执行recordAndReleaseEffect后会将appstate-change方法也清空,之后再发送子应用也接受不到了
6557
+ * 3. 对于this.loadSourceLevel !== 2的情况,unmount是同步执行的,所以也会出现2的问题
6558
+ * TODO: 有可能导致的问题
6559
+ * 1. 在基座接受到afterhidden方法后立即执行unmount,彻底destroy应用时,因为unmount时同步执行,所以this.container为null后才执行cloneContainer
6507
6560
  */
6508
6561
  dispatchCustomEventToMicroApp(this, 'appstate-change', {
6509
6562
  appState: 'afterhidden',
@@ -7345,8 +7398,6 @@ function defineElement(tagName) {
7345
7398
  */
7346
7399
  this.handleAttributeUpdate = () => {
7347
7400
  this.isWaiting = false;
7348
- if (!this.connectStateMap.get(this.connectedCount))
7349
- return;
7350
7401
  const formatAttrName = formatAppName(this.getAttribute('name'));
7351
7402
  const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
7352
7403
  if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
@@ -7456,7 +7507,9 @@ function defineElement(tagName) {
7456
7507
  attributeChangedCallback(attr, _oldVal, newVal) {
7457
7508
  if (this.legalAttribute(attr, newVal) &&
7458
7509
  this[attr === ObservedAttrName.NAME ? 'appName' : 'appUrl'] !== newVal) {
7459
- if (attr === ObservedAttrName.URL && !this.appUrl) {
7510
+ if (attr === ObservedAttrName.URL && (!this.appUrl ||
7511
+ !this.connectStateMap.get(this.connectedCount) // TODO: 这里的逻辑可否再优化一下
7512
+ )) {
7460
7513
  newVal = formatAppURL(newVal, this.appName);
7461
7514
  if (!newVal) {
7462
7515
  return logError(`Invalid attribute url ${newVal}`, this.appName);
@@ -7464,11 +7517,14 @@ function defineElement(tagName) {
7464
7517
  this.appUrl = newVal;
7465
7518
  this.handleInitialNameAndUrl();
7466
7519
  }
7467
- else if (attr === ObservedAttrName.NAME && !this.appName) {
7520
+ else if (attr === ObservedAttrName.NAME && (!this.appName ||
7521
+ !this.connectStateMap.get(this.connectedCount) // TODO: 这里的逻辑可否再优化一下
7522
+ )) {
7468
7523
  const formatNewName = formatAppName(newVal);
7469
7524
  if (!formatNewName) {
7470
7525
  return logError(`Invalid attribute name ${newVal}`, this.appName);
7471
7526
  }
7527
+ // TODO: 当micro-app还未插入文档中就修改name,逻辑可否再优化一下
7472
7528
  if (this.cacheData) {
7473
7529
  microApp.setData(formatNewName, this.cacheData);
7474
7530
  this.cacheData = null;
@@ -7615,6 +7671,7 @@ function defineElement(tagName) {
7615
7671
  inline: this.getDisposeResult('inline'),
7616
7672
  iframe: this.getDisposeResult('iframe'),
7617
7673
  ssrUrl: this.ssrUrl,
7674
+ routerMode: this.getMemoryRouterMode(),
7618
7675
  });
7619
7676
  };
7620
7677
  /**
@@ -7647,7 +7704,12 @@ function defineElement(tagName) {
7647
7704
  */
7648
7705
  handleMount(app) {
7649
7706
  app.isPrefetch = false;
7650
- // TODO: Can defer be removed?
7707
+ /**
7708
+ * Fix error when navigate before app.mount by microApp.router.push(...)
7709
+ * Issue: https://github.com/micro-zoe/micro-app/issues/908
7710
+ */
7711
+ app.setAppState(appStates.BEFORE_MOUNT);
7712
+ // exec mount async, simulate the first render scene
7651
7713
  defer(() => this.mount(app));
7652
7714
  }
7653
7715
  /**
@@ -8186,6 +8248,7 @@ class MicroApp extends EventCenterForBaseApp {
8186
8248
  constructor() {
8187
8249
  super(...arguments);
8188
8250
  this.tagName = 'micro-app';
8251
+ this.hasInit = false;
8189
8252
  this.options = {};
8190
8253
  this.router = router;
8191
8254
  this.preFetch = preFetch;
@@ -8201,6 +8264,15 @@ class MicroApp extends EventCenterForBaseApp {
8201
8264
  if (!isBrowser || !window.customElements) {
8202
8265
  return logError('micro-app is not supported in this environment');
8203
8266
  }
8267
+ /**
8268
+ * TODO: 优化代码和逻辑
8269
+ * 1、同一个基座中initGlobalEnv不能被多次执行,否则会导致死循环
8270
+ * 2、判断逻辑是否放在initGlobalEnv中合适?--- 不合适
8271
+ */
8272
+ if (this.hasInit) {
8273
+ return logError('microApp.start executed repeatedly');
8274
+ }
8275
+ this.hasInit = true;
8204
8276
  if (options === null || options === void 0 ? void 0 : options.tagName) {
8205
8277
  if (/^micro-app(-\S+)?/.test(options.tagName)) {
8206
8278
  this.tagName = options.tagName;