@micro-zoe/micro-app 1.0.0-beta.6 → 1.0.0-beta.7

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.6';
1
+ const version = '1.0.0-beta.7';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -1394,6 +1394,7 @@ var appStates;
1394
1394
  appStates["CREATED"] = "created";
1395
1395
  appStates["LOADING"] = "loading";
1396
1396
  appStates["LOAD_FAILED"] = "load_failed";
1397
+ appStates["BEFORE_MOUNT"] = "before_mount";
1397
1398
  appStates["MOUNTING"] = "mounting";
1398
1399
  appStates["MOUNTED"] = "mounted";
1399
1400
  appStates["UNMOUNT"] = "unmount";
@@ -3509,7 +3510,7 @@ function isEffectiveApp(appName) {
3509
3510
  const app = appInstanceMap.get(appName);
3510
3511
  /**
3511
3512
  * !!(app && !app.isPrefetch && !app.isHidden())
3512
- * TODO: 隐藏的keep-alive应用暂时不作为无效应用,原因如下
3513
+ * NOTE: 隐藏的keep-alive应用暂时不作为无效应用,原因如下
3513
3514
  * 1、隐藏后才执行去除浏览器上的微应用的路由信息的操作,导致微应用的路由信息无法去除
3514
3515
  * 2、如果保持隐藏应用内部正常跳转,阻止同步路由信息到浏览器,这样理论上是好的,但是对于location跳转改如何处理?location跳转是基于修改浏览器地址后发送popstate事件实现的,所以应该是在隐藏后不支持通过location进行跳转
3515
3516
  */
@@ -3865,6 +3866,39 @@ function createRouterApi() {
3865
3866
  // clear element scope after navigate
3866
3867
  removeDomScope();
3867
3868
  }
3869
+ /**
3870
+ * navigation handler
3871
+ * @param appName app.name
3872
+ * @param app app instance
3873
+ * @param to router target options
3874
+ * @param replace use router.replace?
3875
+ */
3876
+ function handleNavigate(appName, app, to, replace) {
3877
+ const microLocation = app.sandBox.proxyWindow.location;
3878
+ const targetLocation = createURL(to.path, microLocation.href);
3879
+ // Only get path data, even if the origin is different from microApp
3880
+ const currentFullPath = microLocation.pathname + microLocation.search + microLocation.hash;
3881
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3882
+ if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
3883
+ const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3884
+ navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3885
+ /**
3886
+ * TODO:
3887
+ * 1. 关闭虚拟路由的跳转地址不同:baseRoute + 子应用地址,文档中要说明
3888
+ * 2. 关闭虚拟路由时跳转方式不同:1、基座跳转但不发送popstate事件 2、控制子应用更新location,内部发送popstate事件。
3889
+ * 核心思路:减小对基座的影响(子应用跳转不向基座发送popstate事件,其他操作一致),但这是必要的吗,只是多了一个触发popstate的操作
3890
+ * 路由优化方案有两种:
3891
+ * 1、减少对基座的影响,主要是解决vue循环刷新的问题
3892
+ * 2、全局发送popstate事件,解决主、子都是vue3的冲突问题
3893
+ * 两者选一个吧,如果选2,则下面这两行代码可以去掉
3894
+ * NOTE1: history和search模式采用2,这样可以解决vue3的问题,custom采用1,避免vue循环刷新的问题,这样在用户出现问题时各有解决方案。但反过来说,每种方案又分别导致另外的问题,不统一,导致复杂度增高
3895
+ * NOTE2: 关闭虚拟路由,同时发送popstate事件还是无法解决vue3的问题(毕竟history.state理论上还是会冲突),那么就没必要发送popstate事件了。
3896
+ */
3897
+ if (isRouterModeCustom(appName)) {
3898
+ updateMicroLocationWithEvent(appName, targetFullPath);
3899
+ }
3900
+ }
3901
+ }
3868
3902
  /**
3869
3903
  * create method of router.push/replace
3870
3904
  * NOTE:
@@ -3883,33 +3917,16 @@ function createRouterApi() {
3883
3917
  * 1. prerender app or hidden keep-alive app clear and record popstate event, so we cannot control app jump through the API
3884
3918
  * 2. disable memory-router
3885
3919
  */
3920
+ /**
3921
+ * TODO: 子应用开始渲染但是还没渲染完成
3922
+ * 1、调用跳转改如何处理
3923
+ * 2、iframe的沙箱还没初始化时执行跳转报错,如何处理。。。
3924
+ * 3、hidden app 是否支持跳转
3925
+ */
3886
3926
  if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName)) {
3887
3927
  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
- }
3928
+ const navigateAction = () => handleNavigate(appName, app, to, replace);
3929
+ app.iframe ? app.sandBox.sandboxReady.then(navigateAction) : navigateAction();
3913
3930
  }
3914
3931
  else {
3915
3932
  logWarn('navigation failed, app does not exist or is inactive');
@@ -4639,6 +4656,8 @@ class WithSandBox {
4639
4656
  this.adapter = new Adapter();
4640
4657
  // get scopeProperties and escapeProperties from plugins
4641
4658
  this.getSpecialProperties(appName);
4659
+ // create location, history for child app
4660
+ this.patchRouter(appName, url, this.microAppWindow);
4642
4661
  // patch window of child app
4643
4662
  this.windowEffect = patchWindow(appName, this.microAppWindow, this);
4644
4663
  // patch document of child app
@@ -4658,10 +4677,6 @@ class WithSandBox {
4658
4677
  return;
4659
4678
  this.active = true;
4660
4679
  /* --- 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
4680
  // update microLocation, attach route info to browser url
4666
4681
  this.initRouteState(defaultPage);
4667
4682
  // unique listener of popstate event for sub app
@@ -4983,7 +4998,7 @@ class WithSandBox {
4983
4998
  });
4984
4999
  }
4985
5000
  // set location & history for memory router
4986
- setMicroAppRouter(appName, url, microAppWindow) {
5001
+ patchRouter(appName, url, microAppWindow) {
4987
5002
  const { microLocation, microHistory } = createMicroRouter(appName, url);
4988
5003
  rawDefineProperties(microAppWindow, {
4989
5004
  location: {
@@ -5036,7 +5051,7 @@ class WithSandBox {
5036
5051
  }
5037
5052
  WithSandBox.activeCount = 0; // number of active sandbox
5038
5053
 
5039
- function patchRoute(appName, url, microAppWindow, browserHost) {
5054
+ function patchRouter(appName, url, microAppWindow, browserHost) {
5040
5055
  const childStaticLocation = new URL(url);
5041
5056
  const childHost = childStaticLocation.protocol + '//' + childStaticLocation.host;
5042
5057
  const childFullPath = childStaticLocation.pathname + childStaticLocation.search + childStaticLocation.hash;
@@ -5275,6 +5290,15 @@ function patchDocumentPrototype(appName, microAppWindow) {
5275
5290
  const rawMicroGetElementsByClassName = microRootDocument.prototype.getElementsByClassName;
5276
5291
  const rawMicroGetElementsByTagName = microRootDocument.prototype.getElementsByTagName;
5277
5292
  const rawMicroGetElementsByName = microRootDocument.prototype.getElementsByName;
5293
+ const rawMicroElementFromPoint = microRootDocument.prototype.elementFromPoint;
5294
+ const rawMicroCaretRangeFromPoint = microRootDocument.prototype.caretRangeFromPoint;
5295
+ microRootDocument.prototype.caretRangeFromPoint = function caretRangeFromPoint(x, y) {
5296
+ // 这里this指向document才可以获取到子应用的document实例,range才可以被成功生成
5297
+ const element = rawMicroElementFromPoint.call(rawDocument, x, y);
5298
+ const range = rawMicroCaretRangeFromPoint.call(rawDocument, x, y);
5299
+ updateElementInfo(element, appName);
5300
+ return range;
5301
+ };
5278
5302
  microRootDocument.prototype.createElement = function createElement(tagName, options) {
5279
5303
  const element = rawMicroCreateElement.call(this, tagName, options);
5280
5304
  return updateElementInfo(element, appName);
@@ -5790,13 +5814,12 @@ class IframeSandbox {
5790
5814
  this.deleteIframeElement = this.createIframeElement(appName, browserHost);
5791
5815
  this.microAppWindow = this.iframe.contentWindow;
5792
5816
  this.patchIframe(this.microAppWindow, (resolve) => {
5793
- // TODO: 优化代码
5794
5817
  // create new html to iframe
5795
5818
  this.createIframeTemplate(this.microAppWindow);
5796
5819
  // get escapeProperties from plugins
5797
5820
  this.getSpecialProperties(appName);
5798
5821
  // patch location & history of child app
5799
- this.proxyLocation = patchRoute(appName, url, this.microAppWindow, browserHost);
5822
+ this.proxyLocation = patchRouter(appName, url, this.microAppWindow, browserHost);
5800
5823
  // patch window of child app
5801
5824
  this.windowEffect = patchWindow$1(appName, this.microAppWindow, this);
5802
5825
  // patch document of child app
@@ -5807,7 +5830,7 @@ class IframeSandbox {
5807
5830
  * create static properties
5808
5831
  * NOTE:
5809
5832
  * 1. execute as early as possible
5810
- * 2. run after patchRoute & createProxyWindow
5833
+ * 2. run after patchRouter & createProxyWindow
5811
5834
  */
5812
5835
  this.initStaticGlobalKeys(appName, url);
5813
5836
  resolve();
@@ -5914,7 +5937,7 @@ class IframeSandbox {
5914
5937
  * create static properties
5915
5938
  * NOTE:
5916
5939
  * 1. execute as early as possible
5917
- * 2. run after patchRoute & createProxyWindow
5940
+ * 2. run after patchRouter & createProxyWindow
5918
5941
  */
5919
5942
  initStaticGlobalKeys(appName, url) {
5920
5943
  this.microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
@@ -6123,7 +6146,7 @@ IframeSandbox.activeCount = 0; // number of active sandbox
6123
6146
  // micro app instances
6124
6147
  const appInstanceMap = new Map();
6125
6148
  class CreateApp {
6126
- constructor({ name, url, container, scopecss, useSandbox, inline, iframe, ssrUrl, isPrefetch, prefetchLevel, }) {
6149
+ constructor({ name, url, container, scopecss, useSandbox, inline, iframe, ssrUrl, isPrefetch, prefetchLevel, routerMode, }) {
6127
6150
  this.state = appStates.CREATED;
6128
6151
  this.keepAliveState = null;
6129
6152
  this.loadSourceLevel = 0;
@@ -6133,7 +6156,6 @@ class CreateApp {
6133
6156
  // TODO: 类型优化,加上iframe沙箱
6134
6157
  this.sandBox = null;
6135
6158
  this.fiber = false;
6136
- this.routerMode = DEFAULT_ROUTER_MODE;
6137
6159
  appInstanceMap.set(name, this);
6138
6160
  // init actions
6139
6161
  this.name = name;
@@ -6142,6 +6164,11 @@ class CreateApp {
6142
6164
  this.scopecss = this.useSandbox && scopecss;
6143
6165
  this.inline = inline !== null && inline !== void 0 ? inline : false;
6144
6166
  this.iframe = iframe !== null && iframe !== void 0 ? iframe : false;
6167
+ /**
6168
+ * NOTE:
6169
+ * 1. Navigate after micro-app created, before mount
6170
+ */
6171
+ this.routerMode = routerMode || DEFAULT_ROUTER_MODE;
6145
6172
  // not exist when prefetch 👇
6146
6173
  this.container = container !== null && container !== void 0 ? container : null;
6147
6174
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
@@ -6237,6 +6264,7 @@ class CreateApp {
6237
6264
  return this.setAppState(appStates.LOADING);
6238
6265
  }
6239
6266
  this.createSandbox();
6267
+ this.setAppState(appStates.BEFORE_MOUNT);
6240
6268
  const nextAction = () => {
6241
6269
  var _a, _b, _c, _d, _e, _f, _g;
6242
6270
  /**
@@ -6350,7 +6378,10 @@ class CreateApp {
6350
6378
  if (isPromise(umdHookMountResult)) {
6351
6379
  umdHookMountResult
6352
6380
  .then(() => this.dispatchMountedEvent())
6353
- .catch(() => this.dispatchMountedEvent());
6381
+ .catch((e) => {
6382
+ logError('An error occurred in window.mount \n', this.name, e);
6383
+ this.dispatchMountedEvent();
6384
+ });
6354
6385
  }
6355
6386
  else {
6356
6387
  this.dispatchMountedEvent();
@@ -7615,6 +7646,7 @@ function defineElement(tagName) {
7615
7646
  inline: this.getDisposeResult('inline'),
7616
7647
  iframe: this.getDisposeResult('iframe'),
7617
7648
  ssrUrl: this.ssrUrl,
7649
+ routerMode: this.getMemoryRouterMode(),
7618
7650
  });
7619
7651
  };
7620
7652
  /**
@@ -7647,7 +7679,12 @@ function defineElement(tagName) {
7647
7679
  */
7648
7680
  handleMount(app) {
7649
7681
  app.isPrefetch = false;
7650
- // TODO: Can defer be removed?
7682
+ /**
7683
+ * Fix error when navigate before app.mount by microApp.router.push(...)
7684
+ * Issue: https://github.com/micro-zoe/micro-app/issues/908
7685
+ */
7686
+ app.setAppState(appStates.BEFORE_MOUNT);
7687
+ // exec mount async, simulate the first render scene
7651
7688
  defer(() => this.mount(app));
7652
7689
  }
7653
7690
  /**