@micro-zoe/micro-app 0.8.6 → 1.0.0-alpha.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/README.md +3 -3
- package/README.zh-cn.md +9 -15
- package/lib/index.d.ts +64 -20
- package/lib/index.esm.js +1291 -359
- package/lib/index.esm.js.map +1 -1
- package/lib/index.min.js +1 -1
- package/lib/index.min.js.map +1 -1
- package/lib/index.umd.js +1 -1
- package/lib/index.umd.js.map +1 -1
- package/package.json +1 -3
- package/typings/global.d.ts +144 -16
package/lib/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const version = '0.
|
|
1
|
+
const version = '1.0.0-alpha.0';
|
|
2
2
|
// do not use isUndefined
|
|
3
3
|
const isBrowser = typeof window !== 'undefined';
|
|
4
4
|
// do not use isUndefined
|
|
@@ -7,10 +7,24 @@ const globalThis = (typeof global !== 'undefined')
|
|
|
7
7
|
: ((typeof window !== 'undefined')
|
|
8
8
|
? window
|
|
9
9
|
: ((typeof self !== 'undefined') ? self : Function('return this')()));
|
|
10
|
+
const noop = () => { };
|
|
11
|
+
const noopFalse = () => false;
|
|
12
|
+
// Array.isArray
|
|
13
|
+
const isArray = Array.isArray;
|
|
14
|
+
// Object.assign
|
|
15
|
+
const assign = Object.assign;
|
|
16
|
+
// Object prototype methods
|
|
17
|
+
const rawDefineProperty = Object.defineProperty;
|
|
18
|
+
const rawDefineProperties = Object.defineProperties;
|
|
19
|
+
const rawHasOwnProperty = Object.prototype.hasOwnProperty;
|
|
10
20
|
// is Undefined
|
|
11
21
|
function isUndefined(target) {
|
|
12
22
|
return target === undefined;
|
|
13
23
|
}
|
|
24
|
+
// is Null
|
|
25
|
+
function isNull(target) {
|
|
26
|
+
return target === null;
|
|
27
|
+
}
|
|
14
28
|
// is String
|
|
15
29
|
function isString(target) {
|
|
16
30
|
return typeof target === 'string';
|
|
@@ -23,8 +37,6 @@ function isBoolean(target) {
|
|
|
23
37
|
function isFunction(target) {
|
|
24
38
|
return typeof target === 'function';
|
|
25
39
|
}
|
|
26
|
-
// is Array
|
|
27
|
-
const isArray = Array.isArray;
|
|
28
40
|
// is PlainObject
|
|
29
41
|
function isPlainObject(target) {
|
|
30
42
|
return toString.call(target) === '[object Object]';
|
|
@@ -41,9 +53,9 @@ function isBoundFunction(target) {
|
|
|
41
53
|
function isShadowRoot(target) {
|
|
42
54
|
return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
|
|
43
55
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
function isURL(target) {
|
|
57
|
+
return target instanceof URL;
|
|
58
|
+
}
|
|
47
59
|
/**
|
|
48
60
|
* format error log
|
|
49
61
|
* @param msg message
|
|
@@ -80,6 +92,16 @@ function logWarn(msg, appName = null, ...rest) {
|
|
|
80
92
|
function defer(fn, ...args) {
|
|
81
93
|
Promise.resolve().then(fn.bind(null, ...args));
|
|
82
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* create URL as MicroLocation
|
|
97
|
+
*/
|
|
98
|
+
const createURL = (function () {
|
|
99
|
+
class Location extends URL {
|
|
100
|
+
}
|
|
101
|
+
return (path, base) => {
|
|
102
|
+
return (base ? new Location('' + path, base) : new Location('' + path));
|
|
103
|
+
};
|
|
104
|
+
})();
|
|
83
105
|
/**
|
|
84
106
|
* Add address protocol
|
|
85
107
|
* @param url address
|
|
@@ -97,7 +119,7 @@ function formatAppURL(url, appName = null) {
|
|
|
97
119
|
if (!isString(url) || !url)
|
|
98
120
|
return '';
|
|
99
121
|
try {
|
|
100
|
-
const { origin, pathname, search } =
|
|
122
|
+
const { origin, pathname, search } = createURL(addProtocol(url));
|
|
101
123
|
// If it ends with .html/.node/.php/.net/.etc, don’t need to add /
|
|
102
124
|
if (/\.(\w+)$/.test(pathname)) {
|
|
103
125
|
return `${origin}${pathname}${search}`;
|
|
@@ -118,6 +140,7 @@ function formatAppURL(url, appName = null) {
|
|
|
118
140
|
* 3. event_center -> EventCenterForBaseApp -> all methods
|
|
119
141
|
* 4. preFetch
|
|
120
142
|
* 5. plugins
|
|
143
|
+
* 6. router api (push, replace)
|
|
121
144
|
*/
|
|
122
145
|
function formatAppName(name) {
|
|
123
146
|
if (!isString(name) || !name)
|
|
@@ -129,7 +152,7 @@ function formatAppName(name) {
|
|
|
129
152
|
* @param url app.url
|
|
130
153
|
*/
|
|
131
154
|
function getEffectivePath(url) {
|
|
132
|
-
const { origin, pathname } =
|
|
155
|
+
const { origin, pathname } = createURL(url);
|
|
133
156
|
if (/\.(\w+)$/.test(pathname)) {
|
|
134
157
|
const fullPath = `${origin}${pathname}`;
|
|
135
158
|
const pathArr = fullPath.split('/');
|
|
@@ -148,7 +171,7 @@ function CompletionPath(path, baseURI) {
|
|
|
148
171
|
/^((((ht|f)tps?)|file):)?\/\//.test(path) ||
|
|
149
172
|
/^(data|blob):/.test(path))
|
|
150
173
|
return path;
|
|
151
|
-
return
|
|
174
|
+
return createURL(path, getEffectivePath(addProtocol(baseURI))).toString();
|
|
152
175
|
}
|
|
153
176
|
/**
|
|
154
177
|
* Get the folder where the link resource is located,
|
|
@@ -306,6 +329,98 @@ function trim(str) {
|
|
|
306
329
|
function isFireFox() {
|
|
307
330
|
return navigator.userAgent.indexOf('Firefox') > -1;
|
|
308
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Transforms a queryString into object.
|
|
334
|
+
* @param search - search string to parse
|
|
335
|
+
* @returns a query object
|
|
336
|
+
*/
|
|
337
|
+
function parseQuery(search) {
|
|
338
|
+
const result = {};
|
|
339
|
+
const queryList = search.split('&');
|
|
340
|
+
// we will not decode the key/value to ensure that the values are consistent when update URL
|
|
341
|
+
for (const queryItem of queryList) {
|
|
342
|
+
const eqPos = queryItem.indexOf('=');
|
|
343
|
+
const key = eqPos < 0 ? queryItem : queryItem.slice(0, eqPos);
|
|
344
|
+
const value = eqPos < 0 ? null : queryItem.slice(eqPos + 1);
|
|
345
|
+
if (key in result) {
|
|
346
|
+
let currentValue = result[key];
|
|
347
|
+
if (!isArray(currentValue)) {
|
|
348
|
+
currentValue = result[key] = [currentValue];
|
|
349
|
+
}
|
|
350
|
+
currentValue.push(value);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
result[key] = value;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Transforms an object to query string
|
|
360
|
+
* @param queryObject - query object to stringify
|
|
361
|
+
* @returns query string without the leading `?`
|
|
362
|
+
*/
|
|
363
|
+
function stringifyQuery(queryObject) {
|
|
364
|
+
let result = '';
|
|
365
|
+
for (const key in queryObject) {
|
|
366
|
+
const value = queryObject[key];
|
|
367
|
+
if (isNull(value)) {
|
|
368
|
+
result += (result.length ? '&' : '') + key;
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
const valueList = isArray(value) ? value : [value];
|
|
372
|
+
valueList.forEach(value => {
|
|
373
|
+
if (!isUndefined(value)) {
|
|
374
|
+
result += (result.length ? '&' : '') + key;
|
|
375
|
+
if (!isNull(value))
|
|
376
|
+
result += '=' + value;
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Register or unregister callback/guard with Set
|
|
385
|
+
*/
|
|
386
|
+
function useSetRecord() {
|
|
387
|
+
const handlers = new Set();
|
|
388
|
+
function add(handler) {
|
|
389
|
+
handlers.add(handler);
|
|
390
|
+
return () => {
|
|
391
|
+
if (handlers.has(handler))
|
|
392
|
+
return handlers.delete(handler);
|
|
393
|
+
return false;
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
add,
|
|
398
|
+
list: () => handlers,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* record data with Map
|
|
403
|
+
*/
|
|
404
|
+
function useMapRecord() {
|
|
405
|
+
const data = new Map();
|
|
406
|
+
function add(key, value) {
|
|
407
|
+
data.set(key, value);
|
|
408
|
+
return () => {
|
|
409
|
+
if (data.has(key))
|
|
410
|
+
return data.delete(key);
|
|
411
|
+
return false;
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
add,
|
|
416
|
+
get: (key) => data.get(key),
|
|
417
|
+
delete: (key) => {
|
|
418
|
+
if (data.has(key))
|
|
419
|
+
return data.delete(key);
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
}
|
|
309
424
|
|
|
310
425
|
var ObservedAttrName;
|
|
311
426
|
(function (ObservedAttrName) {
|
|
@@ -315,13 +430,13 @@ var ObservedAttrName;
|
|
|
315
430
|
// app status
|
|
316
431
|
var appStates;
|
|
317
432
|
(function (appStates) {
|
|
318
|
-
appStates["
|
|
319
|
-
appStates["
|
|
320
|
-
appStates["
|
|
321
|
-
appStates["
|
|
322
|
-
appStates["MOUNTING"] = "
|
|
323
|
-
appStates["MOUNTED"] = "
|
|
324
|
-
appStates["UNMOUNT"] = "
|
|
433
|
+
appStates["CREATED"] = "created";
|
|
434
|
+
appStates["LOADING"] = "loading";
|
|
435
|
+
appStates["LOADED"] = "loaded";
|
|
436
|
+
appStates["LOAD_FAILED"] = "load_failed";
|
|
437
|
+
appStates["MOUNTING"] = "mounting";
|
|
438
|
+
appStates["MOUNTED"] = "mounted";
|
|
439
|
+
appStates["UNMOUNT"] = "unmount";
|
|
325
440
|
})(appStates || (appStates = {}));
|
|
326
441
|
// lifecycles
|
|
327
442
|
var lifeCycles;
|
|
@@ -339,16 +454,16 @@ var lifeCycles;
|
|
|
339
454
|
// keep-alive status
|
|
340
455
|
var keepAliveStates;
|
|
341
456
|
(function (keepAliveStates) {
|
|
342
|
-
keepAliveStates["KEEP_ALIVE_SHOW"] = "
|
|
343
|
-
keepAliveStates["KEEP_ALIVE_HIDDEN"] = "
|
|
457
|
+
keepAliveStates["KEEP_ALIVE_SHOW"] = "keep_alive_show";
|
|
458
|
+
keepAliveStates["KEEP_ALIVE_HIDDEN"] = "keep_alive_hidden";
|
|
344
459
|
})(keepAliveStates || (keepAliveStates = {}));
|
|
345
|
-
const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,Math,Number,Symbol,Date,Promise,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,Document,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,location,
|
|
460
|
+
const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,Math,Number,Symbol,Date,Promise,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,Document,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history,EventSource,fetch,XMLHttpRequest';
|
|
346
461
|
|
|
347
462
|
/**
|
|
348
463
|
* fetch source of html, js, css
|
|
349
464
|
* @param url source path
|
|
350
465
|
* @param appName app name
|
|
351
|
-
* @param config
|
|
466
|
+
* @param config fetch options
|
|
352
467
|
*/
|
|
353
468
|
function fetchSource(url, appName = null, options = {}) {
|
|
354
469
|
if (isFunction(microApp.fetch)) {
|
|
@@ -359,66 +474,6 @@ function fetchSource(url, appName = null, options = {}) {
|
|
|
359
474
|
});
|
|
360
475
|
}
|
|
361
476
|
|
|
362
|
-
class HTMLLoader {
|
|
363
|
-
static getInstance() {
|
|
364
|
-
if (!this.instance) {
|
|
365
|
-
this.instance = new HTMLLoader();
|
|
366
|
-
}
|
|
367
|
-
return this.instance;
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* run logic of load and format html
|
|
371
|
-
* @param successCb success callback
|
|
372
|
-
* @param errorCb error callback, type: (err: Error, meetFetchErr: boolean) => void
|
|
373
|
-
*/
|
|
374
|
-
run(app, successCb) {
|
|
375
|
-
const appName = app.name;
|
|
376
|
-
const htmlUrl = app.ssrUrl || app.url;
|
|
377
|
-
fetchSource(htmlUrl, appName, { cache: 'no-cache' }).then((htmlStr) => {
|
|
378
|
-
if (!htmlStr) {
|
|
379
|
-
const msg = 'html is empty, please check in detail';
|
|
380
|
-
app.onerror(new Error(msg));
|
|
381
|
-
return logError(msg, appName);
|
|
382
|
-
}
|
|
383
|
-
htmlStr = this.formatHTML(htmlUrl, htmlStr, appName);
|
|
384
|
-
successCb(htmlStr, app);
|
|
385
|
-
}).catch((e) => {
|
|
386
|
-
logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, appName, e);
|
|
387
|
-
app.onLoadError(e);
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
formatHTML(htmlUrl, htmlStr, appName) {
|
|
391
|
-
return this.processHtml(htmlUrl, htmlStr, appName, microApp.plugins)
|
|
392
|
-
.replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
|
|
393
|
-
return match
|
|
394
|
-
.replace(/<head/i, '<micro-app-head')
|
|
395
|
-
.replace(/<\/head>/i, '</micro-app-head>');
|
|
396
|
-
})
|
|
397
|
-
.replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
|
|
398
|
-
return match
|
|
399
|
-
.replace(/<body/i, '<micro-app-body')
|
|
400
|
-
.replace(/<\/body>/i, '</micro-app-body>');
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
processHtml(url, code, appName, plugins) {
|
|
404
|
-
var _a;
|
|
405
|
-
if (!plugins)
|
|
406
|
-
return code;
|
|
407
|
-
const mergedPlugins = [];
|
|
408
|
-
plugins.global && mergedPlugins.push(...plugins.global);
|
|
409
|
-
((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]) && mergedPlugins.push(...plugins.modules[appName]);
|
|
410
|
-
if (mergedPlugins.length > 0) {
|
|
411
|
-
return mergedPlugins.reduce((preCode, plugin) => {
|
|
412
|
-
if (isPlainObject(plugin) && isFunction(plugin.processHtml)) {
|
|
413
|
-
return plugin.processHtml(preCode, url, plugin.options);
|
|
414
|
-
}
|
|
415
|
-
return preCode;
|
|
416
|
-
}, code);
|
|
417
|
-
}
|
|
418
|
-
return code;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
477
|
// common reg
|
|
423
478
|
const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/;
|
|
424
479
|
const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/;
|
|
@@ -981,12 +1036,12 @@ function handleNewNode(parent, child, app) {
|
|
|
981
1036
|
return child;
|
|
982
1037
|
}
|
|
983
1038
|
else if (child instanceof HTMLLinkElement) {
|
|
984
|
-
if (child.hasAttribute('exclude')
|
|
1039
|
+
if (child.hasAttribute('exclude')) {
|
|
985
1040
|
const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
|
|
986
1041
|
dynamicElementInMicroAppMap.set(child, linkReplaceComment);
|
|
987
1042
|
return linkReplaceComment;
|
|
988
1043
|
}
|
|
989
|
-
else if (child.hasAttribute('ignore')
|
|
1044
|
+
else if (child.hasAttribute('ignore')) {
|
|
990
1045
|
return child;
|
|
991
1046
|
}
|
|
992
1047
|
const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
|
|
@@ -1034,39 +1089,31 @@ function handleNewNode(parent, child, app) {
|
|
|
1034
1089
|
* @param passiveChild second param of insertBefore and replaceChild
|
|
1035
1090
|
*/
|
|
1036
1091
|
function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
|
|
1037
|
-
const
|
|
1092
|
+
const hijackElement = getHijackElement(parent, app);
|
|
1038
1093
|
/**
|
|
1039
1094
|
* If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
|
|
1040
1095
|
* E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
|
|
1041
1096
|
*/
|
|
1042
|
-
if (
|
|
1097
|
+
if (hijackElement) {
|
|
1043
1098
|
/**
|
|
1044
1099
|
* 1. If passiveChild exists, it must be insertBefore or replaceChild
|
|
1045
1100
|
* 2. When removeChild, targetChild may not be in microAppHead or head
|
|
1046
1101
|
*/
|
|
1047
|
-
if (passiveChild && !
|
|
1048
|
-
return globalEnv.rawAppendChild.call(
|
|
1102
|
+
if (passiveChild && !hijackElement.contains(passiveChild)) {
|
|
1103
|
+
return globalEnv.rawAppendChild.call(hijackElement, targetChild);
|
|
1049
1104
|
}
|
|
1050
|
-
else if (rawMethod === globalEnv.rawRemoveChild && !
|
|
1105
|
+
else if (rawMethod === globalEnv.rawRemoveChild && !hijackElement.contains(targetChild)) {
|
|
1051
1106
|
if (parent.contains(targetChild)) {
|
|
1052
1107
|
return rawMethod.call(parent, targetChild);
|
|
1053
1108
|
}
|
|
1054
1109
|
return targetChild;
|
|
1055
1110
|
}
|
|
1056
|
-
return invokeRawMethod(rawMethod,
|
|
1111
|
+
return invokeRawMethod(rawMethod, hijackElement, targetChild, passiveChild);
|
|
1057
1112
|
}
|
|
1058
1113
|
return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
|
|
1059
1114
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
return rawMethod.call(parent, targetChild);
|
|
1063
|
-
}
|
|
1064
|
-
return rawMethod.call(parent, targetChild, passiveChild);
|
|
1065
|
-
}
|
|
1066
|
-
function isPendMethod(method) {
|
|
1067
|
-
return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
|
|
1068
|
-
}
|
|
1069
|
-
function getContainer(node, app) {
|
|
1115
|
+
// head/body map to micro-app-head/micro-app-body
|
|
1116
|
+
function getHijackElement(node, app) {
|
|
1070
1117
|
var _a, _b;
|
|
1071
1118
|
if (node === document.head) {
|
|
1072
1119
|
return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
|
|
@@ -1076,6 +1123,15 @@ function getContainer(node, app) {
|
|
|
1076
1123
|
}
|
|
1077
1124
|
return null;
|
|
1078
1125
|
}
|
|
1126
|
+
function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
|
|
1127
|
+
if (isPendMethod(rawMethod)) {
|
|
1128
|
+
return rawMethod.call(parent, targetChild);
|
|
1129
|
+
}
|
|
1130
|
+
return rawMethod.call(parent, targetChild, passiveChild);
|
|
1131
|
+
}
|
|
1132
|
+
function isPendMethod(method) {
|
|
1133
|
+
return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
|
|
1134
|
+
}
|
|
1079
1135
|
// Get the map element
|
|
1080
1136
|
function getMappingNode(node) {
|
|
1081
1137
|
var _a;
|
|
@@ -1163,27 +1219,6 @@ function patchElementPrototypeMethods() {
|
|
|
1163
1219
|
this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
|
|
1164
1220
|
return clonedNode;
|
|
1165
1221
|
};
|
|
1166
|
-
// patch getBoundingClientRect
|
|
1167
|
-
// TODO: scenes test
|
|
1168
|
-
// Element.prototype.getBoundingClientRect = function getBoundingClientRect () {
|
|
1169
|
-
// const rawRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(this)
|
|
1170
|
-
// if (this.__MICRO_APP_NAME__) {
|
|
1171
|
-
// const app = appInstanceMap.get(this.__MICRO_APP_NAME__)
|
|
1172
|
-
// if (!app?.container) {
|
|
1173
|
-
// return rawRect
|
|
1174
|
-
// }
|
|
1175
|
-
// const appBody = app.container.querySelector('micro-app-body')
|
|
1176
|
-
// const appBodyRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(appBody)
|
|
1177
|
-
// const computedRect: DOMRect = new DOMRect(
|
|
1178
|
-
// rawRect.x - appBodyRect.x,
|
|
1179
|
-
// rawRect.y - appBodyRect.y,
|
|
1180
|
-
// rawRect.width,
|
|
1181
|
-
// rawRect.height,
|
|
1182
|
-
// )
|
|
1183
|
-
// return computedRect
|
|
1184
|
-
// }
|
|
1185
|
-
// return rawRect
|
|
1186
|
-
// }
|
|
1187
1222
|
}
|
|
1188
1223
|
/**
|
|
1189
1224
|
* Mark the newly created element in the micro application
|
|
@@ -1213,27 +1248,29 @@ function patchDocument() {
|
|
|
1213
1248
|
};
|
|
1214
1249
|
// query element👇
|
|
1215
1250
|
function querySelector(selectors) {
|
|
1216
|
-
var _a, _b, _c;
|
|
1251
|
+
var _a, _b, _c, _d;
|
|
1217
1252
|
const appName = getCurrentAppName();
|
|
1218
1253
|
if (!appName ||
|
|
1254
|
+
!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
|
|
1219
1255
|
!selectors ||
|
|
1220
1256
|
isUniqueElement(selectors) ||
|
|
1221
1257
|
// see https://github.com/micro-zoe/micro-app/issues/56
|
|
1222
1258
|
rawDocument !== this) {
|
|
1223
1259
|
return globalEnv.rawQuerySelector.call(this, selectors);
|
|
1224
1260
|
}
|
|
1225
|
-
return (
|
|
1261
|
+
return (_d = (_c = (_b = appInstanceMap.get(appName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelector(selectors)) !== null && _d !== void 0 ? _d : null;
|
|
1226
1262
|
}
|
|
1227
1263
|
function querySelectorAll(selectors) {
|
|
1228
|
-
var _a, _b, _c;
|
|
1264
|
+
var _a, _b, _c, _d;
|
|
1229
1265
|
const appName = getCurrentAppName();
|
|
1230
1266
|
if (!appName ||
|
|
1267
|
+
!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
|
|
1231
1268
|
!selectors ||
|
|
1232
1269
|
isUniqueElement(selectors) ||
|
|
1233
1270
|
rawDocument !== this) {
|
|
1234
1271
|
return globalEnv.rawQuerySelectorAll.call(this, selectors);
|
|
1235
1272
|
}
|
|
1236
|
-
return (
|
|
1273
|
+
return (_d = (_c = (_b = appInstanceMap.get(appName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelectorAll(selectors)) !== null && _d !== void 0 ? _d : [];
|
|
1237
1274
|
}
|
|
1238
1275
|
Document.prototype.querySelector = querySelector;
|
|
1239
1276
|
Document.prototype.querySelectorAll = querySelectorAll;
|
|
@@ -1301,10 +1338,9 @@ function patchSetAttribute() {
|
|
|
1301
1338
|
if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
|
|
1302
1339
|
if (isPlainObject(value)) {
|
|
1303
1340
|
const cloneValue = {};
|
|
1304
|
-
Object.getOwnPropertyNames(value).forEach((
|
|
1305
|
-
if (!(isString(
|
|
1306
|
-
|
|
1307
|
-
cloneValue[propertyKey] = value[propertyKey];
|
|
1341
|
+
Object.getOwnPropertyNames(value).forEach((key) => {
|
|
1342
|
+
if (!(isString(key) && key.indexOf('__') === 0)) {
|
|
1343
|
+
cloneValue[key] = value[key];
|
|
1308
1344
|
}
|
|
1309
1345
|
});
|
|
1310
1346
|
this.data = cloneValue;
|
|
@@ -1351,7 +1387,6 @@ function releasePatches() {
|
|
|
1351
1387
|
Element.prototype.append = globalEnv.rawAppend;
|
|
1352
1388
|
Element.prototype.prepend = globalEnv.rawPrepend;
|
|
1353
1389
|
Element.prototype.cloneNode = globalEnv.rawCloneNode;
|
|
1354
|
-
// Element.prototype.getBoundingClientRect = globalEnv.rawGetBoundingClientRect
|
|
1355
1390
|
}
|
|
1356
1391
|
// Set the style of micro-app-head and micro-app-body
|
|
1357
1392
|
let hasRejectMicroAppStyle = false;
|
|
@@ -1365,53 +1400,6 @@ function rejectMicroAppStyle() {
|
|
|
1365
1400
|
}
|
|
1366
1401
|
}
|
|
1367
1402
|
|
|
1368
|
-
// 管理 app 的单例
|
|
1369
|
-
class AppManager {
|
|
1370
|
-
constructor() {
|
|
1371
|
-
// Todo: appInstanceMap 由 AppManager 来创建,不再由 create_app 管理
|
|
1372
|
-
this.appInstanceMap = appInstanceMap;
|
|
1373
|
-
}
|
|
1374
|
-
static getInstance() {
|
|
1375
|
-
if (!this.instance) {
|
|
1376
|
-
this.instance = new AppManager();
|
|
1377
|
-
}
|
|
1378
|
-
return this.instance;
|
|
1379
|
-
}
|
|
1380
|
-
get(appName) {
|
|
1381
|
-
return this.appInstanceMap.get(appName);
|
|
1382
|
-
}
|
|
1383
|
-
set(appName, app) {
|
|
1384
|
-
this.appInstanceMap.set(appName, app);
|
|
1385
|
-
}
|
|
1386
|
-
getAll() {
|
|
1387
|
-
return Array.from(this.appInstanceMap.values());
|
|
1388
|
-
}
|
|
1389
|
-
clear() {
|
|
1390
|
-
this.appInstanceMap.clear();
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
function unmountNestedApp() {
|
|
1395
|
-
releaseUnmountOfNestedApp();
|
|
1396
|
-
AppManager.getInstance().getAll().forEach(app => {
|
|
1397
|
-
// @ts-ignore
|
|
1398
|
-
app.container && getRootContainer(app.container).disconnectedCallback();
|
|
1399
|
-
});
|
|
1400
|
-
!window.__MICRO_APP_UMD_MODE__ && AppManager.getInstance().clear();
|
|
1401
|
-
}
|
|
1402
|
-
// if micro-app run in micro application, delete all next generation application when unmount event received
|
|
1403
|
-
function listenUmountOfNestedApp() {
|
|
1404
|
-
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
1405
|
-
window.addEventListener('unmount', unmountNestedApp, false);
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
// release listener
|
|
1409
|
-
function releaseUnmountOfNestedApp() {
|
|
1410
|
-
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
1411
|
-
window.removeEventListener('unmount', unmountNestedApp, false);
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
1403
|
const globalEnv = {};
|
|
1416
1404
|
/**
|
|
1417
1405
|
* Note loop nesting
|
|
@@ -1431,7 +1419,6 @@ function initGlobalEnv() {
|
|
|
1431
1419
|
const rawAppend = Element.prototype.append;
|
|
1432
1420
|
const rawPrepend = Element.prototype.prepend;
|
|
1433
1421
|
const rawCloneNode = Element.prototype.cloneNode;
|
|
1434
|
-
// const rawGetBoundingClientRect = Element.prototype.getBoundingClientRect
|
|
1435
1422
|
const rawCreateElement = Document.prototype.createElement;
|
|
1436
1423
|
const rawCreateElementNS = Document.prototype.createElementNS;
|
|
1437
1424
|
const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
|
|
@@ -1465,7 +1452,7 @@ function initGlobalEnv() {
|
|
|
1465
1452
|
const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
|
|
1466
1453
|
// mark current application as base application
|
|
1467
1454
|
window.__MICRO_APP_BASE_APPLICATION__ = true;
|
|
1468
|
-
|
|
1455
|
+
assign(globalEnv, {
|
|
1469
1456
|
// source/patch
|
|
1470
1457
|
rawSetAttribute,
|
|
1471
1458
|
rawAppendChild,
|
|
@@ -1475,7 +1462,6 @@ function initGlobalEnv() {
|
|
|
1475
1462
|
rawAppend,
|
|
1476
1463
|
rawPrepend,
|
|
1477
1464
|
rawCloneNode,
|
|
1478
|
-
// rawGetBoundingClientRect,
|
|
1479
1465
|
rawCreateElement,
|
|
1480
1466
|
rawCreateElementNS,
|
|
1481
1467
|
rawCreateDocumentFragment,
|
|
@@ -1502,8 +1488,6 @@ function initGlobalEnv() {
|
|
|
1502
1488
|
});
|
|
1503
1489
|
// global effect
|
|
1504
1490
|
rejectMicroAppStyle();
|
|
1505
|
-
releaseUnmountOfNestedApp();
|
|
1506
|
-
listenUmountOfNestedApp();
|
|
1507
1491
|
}
|
|
1508
1492
|
}
|
|
1509
1493
|
|
|
@@ -1519,14 +1503,11 @@ const globalScripts = new Map();
|
|
|
1519
1503
|
function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
1520
1504
|
let replaceComment = null;
|
|
1521
1505
|
let src = script.getAttribute('src');
|
|
1522
|
-
if (
|
|
1523
|
-
src = CompletionPath(src, app.url);
|
|
1524
|
-
}
|
|
1525
|
-
if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
|
|
1506
|
+
if (script.hasAttribute('exclude')) {
|
|
1526
1507
|
replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
|
|
1527
1508
|
}
|
|
1528
1509
|
else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type)) ||
|
|
1529
|
-
script.hasAttribute('ignore')
|
|
1510
|
+
script.hasAttribute('ignore')) {
|
|
1530
1511
|
return null;
|
|
1531
1512
|
}
|
|
1532
1513
|
else if ((globalEnv.supportModuleScript && script.noModule) ||
|
|
@@ -1534,6 +1515,7 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
|
1534
1515
|
replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
|
|
1535
1516
|
}
|
|
1536
1517
|
else if (src) { // remote script
|
|
1518
|
+
src = CompletionPath(src, app.url);
|
|
1537
1519
|
const info = {
|
|
1538
1520
|
code: '',
|
|
1539
1521
|
isExternal: true,
|
|
@@ -1583,46 +1565,6 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
|
1583
1565
|
return parent.replaceChild(replaceComment, script);
|
|
1584
1566
|
}
|
|
1585
1567
|
}
|
|
1586
|
-
/**
|
|
1587
|
-
* get assets plugins
|
|
1588
|
-
* @param appName app name
|
|
1589
|
-
*/
|
|
1590
|
-
function getAssetsPlugins(appName) {
|
|
1591
|
-
var _a, _b, _c;
|
|
1592
|
-
const globalPlugins = ((_a = microApp.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
|
|
1593
|
-
const modulePlugins = ((_c = (_b = microApp.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
|
|
1594
|
-
return [...globalPlugins, ...modulePlugins];
|
|
1595
|
-
}
|
|
1596
|
-
/**
|
|
1597
|
-
* whether the url needs to be excluded
|
|
1598
|
-
* @param url css or js link
|
|
1599
|
-
* @param plugins microApp plugins
|
|
1600
|
-
*/
|
|
1601
|
-
function checkExcludeUrl(url, appName) {
|
|
1602
|
-
if (!url)
|
|
1603
|
-
return false;
|
|
1604
|
-
const plugins = getAssetsPlugins(appName) || [];
|
|
1605
|
-
return plugins.some(plugin => {
|
|
1606
|
-
if (!plugin.excludeChecker)
|
|
1607
|
-
return false;
|
|
1608
|
-
return plugin.excludeChecker(url);
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
/**
|
|
1612
|
-
* whether the url needs to be ignore
|
|
1613
|
-
* @param url css or js link
|
|
1614
|
-
* @param plugins microApp plugins
|
|
1615
|
-
*/
|
|
1616
|
-
function checkIgnoreUrl(url, appName) {
|
|
1617
|
-
if (!url)
|
|
1618
|
-
return false;
|
|
1619
|
-
const plugins = getAssetsPlugins(appName) || [];
|
|
1620
|
-
return plugins.some(plugin => {
|
|
1621
|
-
if (!plugin.ignoreChecker)
|
|
1622
|
-
return false;
|
|
1623
|
-
return plugin.ignoreChecker(url);
|
|
1624
|
-
});
|
|
1625
|
-
}
|
|
1626
1568
|
/**
|
|
1627
1569
|
* Get remote resources of script
|
|
1628
1570
|
* @param wrapElement htmlDom
|
|
@@ -1899,10 +1841,10 @@ function flatChildren(parent, app, microAppHead) {
|
|
|
1899
1841
|
});
|
|
1900
1842
|
for (const dom of children) {
|
|
1901
1843
|
if (dom instanceof HTMLLinkElement) {
|
|
1902
|
-
if (dom.hasAttribute('exclude')
|
|
1844
|
+
if (dom.hasAttribute('exclude')) {
|
|
1903
1845
|
parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
|
|
1904
1846
|
}
|
|
1905
|
-
else if (!
|
|
1847
|
+
else if (!dom.hasAttribute('ignore')) {
|
|
1906
1848
|
extractLinkFromHtml(dom, parent, app);
|
|
1907
1849
|
}
|
|
1908
1850
|
else if (dom.hasAttribute('href')) {
|
|
@@ -1956,6 +1898,34 @@ function extractSourceDom(htmlStr, app) {
|
|
|
1956
1898
|
app.onLoad(wrapElement);
|
|
1957
1899
|
}
|
|
1958
1900
|
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Get and format html
|
|
1903
|
+
* @param app app
|
|
1904
|
+
*/
|
|
1905
|
+
function extractHtml(app) {
|
|
1906
|
+
fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
|
|
1907
|
+
if (!htmlStr) {
|
|
1908
|
+
const msg = 'html is empty, please check in detail';
|
|
1909
|
+
app.onerror(new Error(msg));
|
|
1910
|
+
return logError(msg, app.name);
|
|
1911
|
+
}
|
|
1912
|
+
htmlStr = htmlStr
|
|
1913
|
+
.replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
|
|
1914
|
+
return match
|
|
1915
|
+
.replace(/<head/i, '<micro-app-head')
|
|
1916
|
+
.replace(/<\/head>/i, '</micro-app-head>');
|
|
1917
|
+
})
|
|
1918
|
+
.replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
|
|
1919
|
+
return match
|
|
1920
|
+
.replace(/<body/i, '<micro-app-body')
|
|
1921
|
+
.replace(/<\/body>/i, '</micro-app-body>');
|
|
1922
|
+
});
|
|
1923
|
+
extractSourceDom(htmlStr, app);
|
|
1924
|
+
}).catch((e) => {
|
|
1925
|
+
logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
|
|
1926
|
+
app.onLoadError(e);
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1959
1929
|
|
|
1960
1930
|
class EventCenter {
|
|
1961
1931
|
constructor() {
|
|
@@ -2238,6 +2208,28 @@ function rebuildDataCenterSnapshot(microAppEventCenter) {
|
|
|
2238
2208
|
}
|
|
2239
2209
|
}
|
|
2240
2210
|
|
|
2211
|
+
function unmountNestedApp() {
|
|
2212
|
+
appInstanceMap.forEach(app => {
|
|
2213
|
+
// @ts-ignore
|
|
2214
|
+
app.container && getRootContainer(app.container).disconnectedCallback();
|
|
2215
|
+
});
|
|
2216
|
+
!window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
|
|
2217
|
+
}
|
|
2218
|
+
// release listener
|
|
2219
|
+
function releaseUnmountOfNestedApp() {
|
|
2220
|
+
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
2221
|
+
window.removeEventListener('unmount', unmountNestedApp, false);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
// if micro-app run in micro application, delete all next generation application when unmount event received
|
|
2225
|
+
// unmount event will auto release by sandbox
|
|
2226
|
+
function listenUmountOfNestedApp() {
|
|
2227
|
+
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
2228
|
+
releaseUnmountOfNestedApp();
|
|
2229
|
+
window.addEventListener('unmount', unmountNestedApp, false);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2241
2233
|
/* eslint-disable no-return-assign */
|
|
2242
2234
|
function isBoundedFunction(value) {
|
|
2243
2235
|
if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
|
|
@@ -2277,6 +2269,21 @@ function bindFunctionToRawWindow(rawWindow, value) {
|
|
|
2277
2269
|
return value;
|
|
2278
2270
|
}
|
|
2279
2271
|
|
|
2272
|
+
// this events should be sent to the specified app
|
|
2273
|
+
const formatEventList = ['unmount', 'appstate-change'];
|
|
2274
|
+
/**
|
|
2275
|
+
* Format event name
|
|
2276
|
+
* @param eventName event name
|
|
2277
|
+
* @param appName app name
|
|
2278
|
+
*/
|
|
2279
|
+
function formatEventName$1(eventName, appName) {
|
|
2280
|
+
var _a;
|
|
2281
|
+
if (formatEventList.includes(eventName) ||
|
|
2282
|
+
((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter))) {
|
|
2283
|
+
return `${eventName}-${appName}`;
|
|
2284
|
+
}
|
|
2285
|
+
return eventName;
|
|
2286
|
+
}
|
|
2280
2287
|
// document.onclick binding list, the binding function of each application is unique
|
|
2281
2288
|
const documentClickListMap = new Map();
|
|
2282
2289
|
let hasRewriteDocumentOnClick = false;
|
|
@@ -2370,19 +2377,6 @@ function releaseEffectDocumentEvent() {
|
|
|
2370
2377
|
document.addEventListener = globalEnv.rawDocumentAddEventListener;
|
|
2371
2378
|
document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
|
|
2372
2379
|
}
|
|
2373
|
-
// this events should be sent to the specified app
|
|
2374
|
-
const formatEventList = ['unmount', 'appstate-change'];
|
|
2375
|
-
/**
|
|
2376
|
-
* Format event name
|
|
2377
|
-
* @param type event name
|
|
2378
|
-
* @param microAppWindow micro window
|
|
2379
|
-
*/
|
|
2380
|
-
function formatEventType(type, microAppWindow) {
|
|
2381
|
-
if (formatEventList.includes(type)) {
|
|
2382
|
-
return `${type}-${microAppWindow.__MICRO_APP_NAME__}`;
|
|
2383
|
-
}
|
|
2384
|
-
return type;
|
|
2385
|
-
}
|
|
2386
2380
|
/**
|
|
2387
2381
|
* Rewrite side-effect events
|
|
2388
2382
|
* @param microAppWindow micro window
|
|
@@ -2395,7 +2389,7 @@ function effect(microAppWindow) {
|
|
|
2395
2389
|
const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
|
|
2396
2390
|
// listener may be null, e.g test-passive
|
|
2397
2391
|
microAppWindow.addEventListener = function (type, listener, options) {
|
|
2398
|
-
type =
|
|
2392
|
+
type = formatEventName$1(type, appName);
|
|
2399
2393
|
const listenerList = eventListenerMap.get(type);
|
|
2400
2394
|
if (listenerList) {
|
|
2401
2395
|
listenerList.add(listener);
|
|
@@ -2407,7 +2401,7 @@ function effect(microAppWindow) {
|
|
|
2407
2401
|
rawWindowAddEventListener.call(rawWindow, type, listener, options);
|
|
2408
2402
|
};
|
|
2409
2403
|
microAppWindow.removeEventListener = function (type, listener, options) {
|
|
2410
|
-
type =
|
|
2404
|
+
type = formatEventName$1(type, appName);
|
|
2411
2405
|
const listenerList = eventListenerMap.get(type);
|
|
2412
2406
|
if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
|
|
2413
2407
|
listenerList.delete(listener);
|
|
@@ -2534,71 +2528,866 @@ function effect(microAppWindow) {
|
|
|
2534
2528
|
};
|
|
2535
2529
|
}
|
|
2536
2530
|
|
|
2537
|
-
//
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
]
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
this.escapeProperties = [];
|
|
2556
|
-
// Properties newly added to microAppWindow
|
|
2557
|
-
this.injectedKeys = new Set();
|
|
2558
|
-
// Properties escape to rawWindow, cleared when unmount
|
|
2559
|
-
this.escapeKeys = new Set();
|
|
2560
|
-
// sandbox state
|
|
2561
|
-
this.active = false;
|
|
2562
|
-
this.microAppWindow = {}; // Proxy target
|
|
2563
|
-
// get scopeProperties and escapeProperties from plugins
|
|
2564
|
-
this.getSpecialProperties(appName);
|
|
2565
|
-
// create proxyWindow with Proxy(microAppWindow)
|
|
2566
|
-
this.proxyWindow = this.createProxyWindow(appName);
|
|
2567
|
-
// inject global properties
|
|
2568
|
-
this.initMicroAppWindow(this.microAppWindow, appName, url);
|
|
2569
|
-
// Rewrite global event listener & timeout
|
|
2570
|
-
Object.assign(this, effect(this.microAppWindow));
|
|
2571
|
-
}
|
|
2572
|
-
start(baseRoute) {
|
|
2573
|
-
if (!this.active) {
|
|
2574
|
-
this.active = true;
|
|
2575
|
-
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
|
|
2576
|
-
// BUG FIX: bable-polyfill@6.x
|
|
2577
|
-
globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
|
|
2578
|
-
if (++SandBox.activeCount === 1) {
|
|
2579
|
-
effectDocumentEvent();
|
|
2580
|
-
patchElementPrototypeMethods();
|
|
2581
|
-
}
|
|
2531
|
+
// set micro app state to origin state
|
|
2532
|
+
function setMicroState(appName, rawState, microState) {
|
|
2533
|
+
const additionalState = {
|
|
2534
|
+
microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
|
|
2535
|
+
[appName]: microState
|
|
2536
|
+
})
|
|
2537
|
+
};
|
|
2538
|
+
// create new state object
|
|
2539
|
+
return assign({}, rawState, additionalState);
|
|
2540
|
+
}
|
|
2541
|
+
// delete micro app state form origin state
|
|
2542
|
+
function removeMicroState(appName, rawState) {
|
|
2543
|
+
if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
|
|
2544
|
+
if (!isUndefined(rawState.microAppState[appName])) {
|
|
2545
|
+
delete rawState.microAppState[appName];
|
|
2546
|
+
}
|
|
2547
|
+
if (!Object.keys(rawState.microAppState).length) {
|
|
2548
|
+
delete rawState.microAppState;
|
|
2582
2549
|
}
|
|
2583
2550
|
}
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2551
|
+
// 生成新的state对象
|
|
2552
|
+
return assign({}, rawState);
|
|
2553
|
+
}
|
|
2554
|
+
// get micro app state form origin state
|
|
2555
|
+
function getMicroState(appName, state) {
|
|
2556
|
+
var _a;
|
|
2557
|
+
return ((_a = state === null || state === void 0 ? void 0 : state.microAppState) === null || _a === void 0 ? void 0 : _a[appName]) || null;
|
|
2558
|
+
}
|
|
2559
|
+
const ENC_AD_RE = /&/g; // %M1
|
|
2560
|
+
const ENC_EQ_RE = /=/g; // %M2
|
|
2561
|
+
const DEC_AD_RE = /%M1/g; // &
|
|
2562
|
+
const DEC_EQ_RE = /%M2/g; // =
|
|
2563
|
+
function encodeMicroPath(path) {
|
|
2564
|
+
return encodeURIComponent(commonDecode(path).replace(ENC_AD_RE, '%M1').replace(ENC_EQ_RE, '%M2'));
|
|
2565
|
+
}
|
|
2566
|
+
function decodeMicroPath(path) {
|
|
2567
|
+
return commonDecode(path).replace(DEC_AD_RE, '&').replace(DEC_EQ_RE, '=');
|
|
2568
|
+
}
|
|
2569
|
+
function commonDecode(path) {
|
|
2570
|
+
try {
|
|
2571
|
+
const decPath = decodeURIComponent(path);
|
|
2572
|
+
if (path === decPath || DEC_AD_RE.test(decPath) || DEC_EQ_RE.test(decPath))
|
|
2573
|
+
return decPath;
|
|
2574
|
+
return commonDecode(decPath);
|
|
2575
|
+
}
|
|
2576
|
+
catch (_a) {
|
|
2577
|
+
return path;
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
// 格式化query参数key,防止与原有参数的冲突
|
|
2581
|
+
function formatQueryAppName(appName) {
|
|
2582
|
+
return `app-${appName}`;
|
|
2583
|
+
}
|
|
2584
|
+
// 根据浏览器url参数,获取当前子应用的path
|
|
2585
|
+
function getMicroPathFromURL(appName) {
|
|
2586
|
+
var _a, _b;
|
|
2587
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
2588
|
+
const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
|
|
2589
|
+
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)]);
|
|
2590
|
+
return isString(microPath) ? decodeMicroPath(microPath) : null;
|
|
2591
|
+
}
|
|
2592
|
+
// 将name=encodeUrl地址插入到浏览器url上
|
|
2593
|
+
function setMicroPathToURL(appName, microLocation) {
|
|
2594
|
+
let { pathname, search, hash } = globalEnv.rawWindow.location;
|
|
2595
|
+
const queryObject = getQueryObjectFromURL(search, hash);
|
|
2596
|
+
const encodedMicroPath = encodeMicroPath(microLocation.pathname +
|
|
2597
|
+
microLocation.search +
|
|
2598
|
+
microLocation.hash);
|
|
2599
|
+
let isAttach2Hash = false; // 基座是否是hash模式,这个其实也不准,只是表示参数加到了hash上
|
|
2600
|
+
// hash存在且search不存在,则认为是hash路由
|
|
2601
|
+
if (hash && !search) {
|
|
2602
|
+
isAttach2Hash = true;
|
|
2603
|
+
if (queryObject.hashQuery) {
|
|
2604
|
+
queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
|
|
2605
|
+
}
|
|
2606
|
+
else {
|
|
2607
|
+
queryObject.hashQuery = {
|
|
2608
|
+
[formatQueryAppName(appName)]: encodedMicroPath
|
|
2609
|
+
};
|
|
2610
|
+
}
|
|
2611
|
+
const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?';
|
|
2612
|
+
hash = baseHash + stringifyQuery(queryObject.hashQuery);
|
|
2613
|
+
}
|
|
2614
|
+
else {
|
|
2615
|
+
if (queryObject.searchQuery) {
|
|
2616
|
+
queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath;
|
|
2617
|
+
}
|
|
2618
|
+
else {
|
|
2619
|
+
queryObject.searchQuery = {
|
|
2620
|
+
[formatQueryAppName(appName)]: encodedMicroPath
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
search = '?' + stringifyQuery(queryObject.searchQuery);
|
|
2624
|
+
}
|
|
2625
|
+
return {
|
|
2626
|
+
fullPath: pathname + search + hash,
|
|
2627
|
+
isAttach2Hash,
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
// 将name=encodeUrl的参数从浏览器url上删除
|
|
2631
|
+
function removeMicroPathFromURL(appName, targetLocation) {
|
|
2632
|
+
var _a, _b, _c, _d;
|
|
2633
|
+
let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
|
|
2634
|
+
const queryObject = getQueryObjectFromURL(search, hash);
|
|
2635
|
+
let isAttach2Hash = false;
|
|
2636
|
+
if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
|
|
2637
|
+
isAttach2Hash = true;
|
|
2638
|
+
(_b = queryObject.hashQuery) === null || _b === void 0 ? true : delete _b[formatQueryAppName(appName)];
|
|
2639
|
+
const hashQueryStr = stringifyQuery(queryObject.hashQuery);
|
|
2640
|
+
hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr;
|
|
2641
|
+
}
|
|
2642
|
+
else if ((_c = queryObject.searchQuery) === null || _c === void 0 ? void 0 : _c[formatQueryAppName(appName)]) {
|
|
2643
|
+
(_d = queryObject.searchQuery) === null || _d === void 0 ? true : delete _d[formatQueryAppName(appName)];
|
|
2644
|
+
const searchQueryStr = stringifyQuery(queryObject.searchQuery);
|
|
2645
|
+
search = searchQueryStr ? '?' + searchQueryStr : '';
|
|
2646
|
+
}
|
|
2647
|
+
return {
|
|
2648
|
+
fullPath: pathname + search + hash,
|
|
2649
|
+
isAttach2Hash,
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
/**
|
|
2653
|
+
* 根据location获取query对象
|
|
2654
|
+
*/
|
|
2655
|
+
function getQueryObjectFromURL(search, hash) {
|
|
2656
|
+
const queryObject = {};
|
|
2657
|
+
if (search !== '' && search !== '?') {
|
|
2658
|
+
queryObject.searchQuery = parseQuery(search.slice(1));
|
|
2659
|
+
}
|
|
2660
|
+
if (hash.includes('?')) {
|
|
2661
|
+
queryObject.hashQuery = parseQuery(hash.slice(hash.indexOf('?') + 1));
|
|
2662
|
+
}
|
|
2663
|
+
return queryObject;
|
|
2664
|
+
}
|
|
2665
|
+
/**
|
|
2666
|
+
* get microApp path from browser URL without hash
|
|
2667
|
+
*/
|
|
2668
|
+
function getNoHashMicroPathFromURL(appName, baseUrl) {
|
|
2669
|
+
const microPath = getMicroPathFromURL(appName);
|
|
2670
|
+
if (!microPath)
|
|
2671
|
+
return '';
|
|
2672
|
+
const formatLocation = createURL(microPath, baseUrl);
|
|
2673
|
+
return formatLocation.origin + formatLocation.pathname + formatLocation.search;
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
/**
|
|
2677
|
+
* dispatch PopStateEvent & HashChangeEvent to child app
|
|
2678
|
+
* each child app will listen for popstate event when sandbox start
|
|
2679
|
+
* and release it when sandbox stop
|
|
2680
|
+
* @param appName app name
|
|
2681
|
+
* @returns release callback
|
|
2682
|
+
*/
|
|
2683
|
+
function addHistoryListener(appName) {
|
|
2684
|
+
const rawWindow = globalEnv.rawWindow;
|
|
2685
|
+
// handle popstate event and distribute to child app
|
|
2686
|
+
const popStateHandler = (e) => {
|
|
2687
|
+
// exclude hidden keep-alive app
|
|
2688
|
+
if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
|
|
2689
|
+
const microPath = getMicroPathFromURL(appName);
|
|
2690
|
+
const app = appInstanceMap.get(appName);
|
|
2691
|
+
const proxyWindow = app.sandBox.proxyWindow;
|
|
2692
|
+
let isHashChange = false;
|
|
2693
|
+
// for hashChangeEvent
|
|
2694
|
+
const oldHref = proxyWindow.location.href;
|
|
2695
|
+
// Do not attach micro state to url when microPath is empty
|
|
2696
|
+
if (microPath) {
|
|
2697
|
+
const oldHash = proxyWindow.location.hash;
|
|
2698
|
+
updateMicroLocation(appName, microPath, proxyWindow.location);
|
|
2699
|
+
isHashChange = proxyWindow.location.hash !== oldHash;
|
|
2700
|
+
}
|
|
2701
|
+
// dispatch formatted popStateEvent to child
|
|
2702
|
+
dispatchPopStateEventToMicroApp(appName, proxyWindow, rawWindow.history.state);
|
|
2703
|
+
// dispatch formatted hashChangeEvent to child when hash change
|
|
2704
|
+
if (isHashChange)
|
|
2705
|
+
dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref);
|
|
2706
|
+
// clear element scope before trigger event of next app
|
|
2707
|
+
removeDomScope();
|
|
2708
|
+
}
|
|
2709
|
+
};
|
|
2710
|
+
rawWindow.addEventListener('popstate', popStateHandler);
|
|
2711
|
+
return () => {
|
|
2712
|
+
rawWindow.removeEventListener('popstate', popStateHandler);
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
/**
|
|
2716
|
+
* dispatch formatted popstate event to microApp
|
|
2717
|
+
* @param appName app name
|
|
2718
|
+
* @param proxyWindow sandbox window
|
|
2719
|
+
* @param eventState history.state
|
|
2720
|
+
*/
|
|
2721
|
+
function dispatchPopStateEventToMicroApp(appName, proxyWindow, eventState) {
|
|
2722
|
+
// create PopStateEvent named popstate-appName with sub app state
|
|
2723
|
+
const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName, eventState) });
|
|
2724
|
+
globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
|
|
2725
|
+
// call function window.onpopstate if it exists
|
|
2726
|
+
typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
|
|
2727
|
+
}
|
|
2728
|
+
/**
|
|
2729
|
+
* dispatch formatted hashchange event to microApp
|
|
2730
|
+
* @param appName app name
|
|
2731
|
+
* @param proxyWindow sandbox window
|
|
2732
|
+
* @param oldHref old href
|
|
2733
|
+
*/
|
|
2734
|
+
function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
|
|
2735
|
+
const newHashChangeEvent = new HashChangeEvent(formatEventName$1('hashchange', appName), {
|
|
2736
|
+
newURL: proxyWindow.location.href,
|
|
2737
|
+
oldURL: oldHref,
|
|
2738
|
+
});
|
|
2739
|
+
globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
|
|
2740
|
+
// call function window.onhashchange if it exists
|
|
2741
|
+
typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
|
|
2742
|
+
}
|
|
2743
|
+
/**
|
|
2744
|
+
* dispatch native PopStateEvent, simulate location behavior
|
|
2745
|
+
* @param onlyForBrowser only dispatch PopStateEvent to browser
|
|
2746
|
+
*/
|
|
2747
|
+
function dispatchNativePopStateEvent(onlyForBrowser) {
|
|
2748
|
+
const event = new PopStateEvent('popstate', { state: null });
|
|
2749
|
+
if (onlyForBrowser)
|
|
2750
|
+
event.onlyForBrowser = true;
|
|
2751
|
+
globalEnv.rawWindow.dispatchEvent(event);
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* dispatch hashchange event to browser
|
|
2755
|
+
* @param oldHref old href of rawWindow.location
|
|
2756
|
+
*/
|
|
2757
|
+
function dispatchNativeHashChangeEvent(oldHref) {
|
|
2758
|
+
const newHashChangeEvent = new HashChangeEvent('hashchange', {
|
|
2759
|
+
newURL: globalEnv.rawWindow.location.href,
|
|
2760
|
+
oldURL: oldHref,
|
|
2761
|
+
});
|
|
2762
|
+
globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* dispatch popstate & hashchange event to browser
|
|
2766
|
+
* @param onlyForBrowser only dispatch event to browser
|
|
2767
|
+
* @param oldHref old href of rawWindow.location
|
|
2768
|
+
*/
|
|
2769
|
+
function dispatchNativeEvent(onlyForBrowser, oldHref) {
|
|
2770
|
+
// clear element scope before dispatch global event
|
|
2771
|
+
removeDomScope();
|
|
2772
|
+
dispatchNativePopStateEvent(onlyForBrowser);
|
|
2773
|
+
if (oldHref) {
|
|
2774
|
+
dispatchNativeHashChangeEvent(oldHref);
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
/**
|
|
2779
|
+
* create proxyHistory for microApp
|
|
2780
|
+
* MDN https://developer.mozilla.org/en-US/docs/Web/API/History
|
|
2781
|
+
* @param appName app name
|
|
2782
|
+
* @param microLocation microApp location
|
|
2783
|
+
*/
|
|
2784
|
+
function createMicroHistory(appName, microLocation) {
|
|
2785
|
+
const rawHistory = globalEnv.rawWindow.history;
|
|
2786
|
+
function getMicroHistoryMethod(methodName) {
|
|
2787
|
+
return function (...rests) {
|
|
2788
|
+
if ((methodName === 'pushState' || methodName === 'replaceState') &&
|
|
2789
|
+
(isString(rests[2]) || isURL(rests[2]))) {
|
|
2790
|
+
const targetLocation = createURL(rests[2], microLocation.href);
|
|
2791
|
+
if (targetLocation.origin === microLocation.origin) {
|
|
2792
|
+
navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rawHistory.state, rests[0]), rests[1]);
|
|
2793
|
+
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
|
|
2794
|
+
if (targetFullPath !== microLocation.fullPath) {
|
|
2795
|
+
updateMicroLocation(appName, targetFullPath, microLocation);
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
else {
|
|
2799
|
+
rawHistory[methodName].apply(rawHistory, rests);
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
else {
|
|
2803
|
+
rawHistory[methodName].apply(rawHistory, rests);
|
|
2804
|
+
}
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
return new Proxy(rawHistory, {
|
|
2808
|
+
get(target, key) {
|
|
2809
|
+
if (key === 'state') {
|
|
2810
|
+
return getMicroState(appName, rawHistory.state);
|
|
2811
|
+
}
|
|
2812
|
+
else if (isFunction(Reflect.get(target, key))) {
|
|
2813
|
+
return getMicroHistoryMethod(key);
|
|
2814
|
+
}
|
|
2815
|
+
return Reflect.get(target, key);
|
|
2816
|
+
},
|
|
2817
|
+
set(target, key, value) {
|
|
2818
|
+
return Reflect.set(target, key, value);
|
|
2819
|
+
}
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
/**
|
|
2823
|
+
* navigate to new path base on native method of history
|
|
2824
|
+
* @param methodName pushState/replaceState
|
|
2825
|
+
* @param fullPath full path
|
|
2826
|
+
* @param state history.state, default is null
|
|
2827
|
+
* @param title history.title, default is ''
|
|
2828
|
+
*/
|
|
2829
|
+
function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
|
|
2830
|
+
globalEnv.rawWindow.history[methodName](state, title, fullPath);
|
|
2831
|
+
}
|
|
2832
|
+
/**
|
|
2833
|
+
* Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
|
|
2834
|
+
* Use scenes:
|
|
2835
|
+
* 1. mount/unmount through updateBrowserURL with limited popstateEvent
|
|
2836
|
+
* 2. proxyHistory.pushState/replaceState with limited popstateEvent
|
|
2837
|
+
* 3. api microApp.router.push/replace
|
|
2838
|
+
* 4. proxyLocation.hash = xxx
|
|
2839
|
+
* @param methodName pushState/replaceState
|
|
2840
|
+
* @param result result of add/remove microApp path on browser url
|
|
2841
|
+
* @param onlyForBrowser only dispatch event to browser
|
|
2842
|
+
* @param state history.state, not required
|
|
2843
|
+
* @param title history.title, not required
|
|
2844
|
+
*/
|
|
2845
|
+
function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, title) {
|
|
2846
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
2847
|
+
const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
|
|
2848
|
+
const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
|
|
2849
|
+
// navigate with native history method
|
|
2850
|
+
nativeHistoryNavigate(methodName, result.fullPath, state, title);
|
|
2851
|
+
if (oldFullPath !== result.fullPath)
|
|
2852
|
+
dispatchNativeEvent(onlyForBrowser, oldHref);
|
|
2853
|
+
}
|
|
2854
|
+
/**
|
|
2855
|
+
* update browser url when mount/unmount/hidden/show
|
|
2856
|
+
* @param result result of add/remove microApp path on browser url
|
|
2857
|
+
* @param state history.state
|
|
2858
|
+
*/
|
|
2859
|
+
function updateBrowserURL(result, state) {
|
|
2860
|
+
navigateWithNativeEvent('replaceState', result, true, state);
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* When path is same, keep the microAppState in history.state
|
|
2864
|
+
* Fix bug of missing microAppState in next.js & angular
|
|
2865
|
+
* @param method history.pushState/replaceState
|
|
2866
|
+
*/
|
|
2867
|
+
function patchHistoryState(method) {
|
|
2868
|
+
const rawWindow = globalEnv.rawWindow;
|
|
2869
|
+
return function (...rests) {
|
|
2870
|
+
var _a;
|
|
2871
|
+
if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.microAppState) &&
|
|
2872
|
+
(!isPlainObject(rests[0]) || !rests[0].microAppState) &&
|
|
2873
|
+
(isString(rests[2]) || isURL(rests[2]))) {
|
|
2874
|
+
const currentHref = rawWindow.location.href;
|
|
2875
|
+
const targetLocation = createURL(rests[2], currentHref);
|
|
2876
|
+
if (targetLocation.href === currentHref) {
|
|
2877
|
+
rests[0] = assign({}, rests[0], {
|
|
2878
|
+
microAppState: rawWindow.history.state.microAppState,
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
method.apply(rawWindow.history, rests);
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
let isReWriteHistoryState = false;
|
|
2886
|
+
/**
|
|
2887
|
+
* rewrite history.pushState/replaceState
|
|
2888
|
+
* used to fix the problem that the microAppState maybe missing when mainApp navigate to same path
|
|
2889
|
+
* e.g: when nextjs, angular receive popstate event, they will use history.replaceState to update browser url with a new state object
|
|
2890
|
+
*/
|
|
2891
|
+
function rewriteHistoryState() {
|
|
2892
|
+
// filter nest app
|
|
2893
|
+
if (!isReWriteHistoryState && !window.__MICRO_APP_ENVIRONMENT__) {
|
|
2894
|
+
isReWriteHistoryState = true;
|
|
2895
|
+
const rawWindow = globalEnv.rawWindow;
|
|
2896
|
+
rawWindow.history.pushState = patchHistoryState(rawWindow.history.pushState);
|
|
2897
|
+
rawWindow.history.replaceState = patchHistoryState(rawWindow.history.replaceState);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
function createRouterApi() {
|
|
2902
|
+
/**
|
|
2903
|
+
* common handler for router.push/router.replace method
|
|
2904
|
+
* @param appName app name
|
|
2905
|
+
* @param methodName replaceState/pushState
|
|
2906
|
+
* @param targetLocation target location
|
|
2907
|
+
* @param state to.state
|
|
2908
|
+
*/
|
|
2909
|
+
function navigateWithRawHistory(appName, methodName, targetLocation, state) {
|
|
2910
|
+
navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, globalEnv.rawWindow.history.state, state !== null && state !== void 0 ? state : null));
|
|
2911
|
+
// clear element scope after navigate
|
|
2912
|
+
removeDomScope();
|
|
2913
|
+
}
|
|
2914
|
+
/**
|
|
2915
|
+
* create method of router.push/replace
|
|
2916
|
+
* NOTE:
|
|
2917
|
+
* 1. The same fullPath will be blocked
|
|
2918
|
+
* 2. name & path is required
|
|
2919
|
+
* 3. path is fullPath except for the domain (the domain can be taken, but not valid)
|
|
2920
|
+
* @param replace use router.replace?
|
|
2921
|
+
*/
|
|
2922
|
+
function createNavigationMethod(replace) {
|
|
2923
|
+
return function (to) {
|
|
2924
|
+
const appName = formatAppName(to.name);
|
|
2925
|
+
// console.log(3333333, appInstanceMap.get(appName))
|
|
2926
|
+
if (appName && isString(to.path)) {
|
|
2927
|
+
const app = appInstanceMap.get(appName);
|
|
2928
|
+
if (app && !app.sandBox)
|
|
2929
|
+
return logError(`navigation failed, sandBox of app ${appName} is closed`);
|
|
2930
|
+
// active apps, include hidden keep-alive app
|
|
2931
|
+
if (getActiveApps().includes(appName)) {
|
|
2932
|
+
const microLocation = app.sandBox.proxyWindow.location;
|
|
2933
|
+
const targetLocation = createURL(to.path, microLocation.href);
|
|
2934
|
+
// Only get path data, even if the origin is different from microApp
|
|
2935
|
+
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
|
|
2936
|
+
if (microLocation.fullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
|
|
2937
|
+
const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
|
|
2938
|
+
navigateWithRawHistory(appName, methodName, targetLocation, to.state);
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
else {
|
|
2942
|
+
/**
|
|
2943
|
+
* app not exit or unmounted, update browser URL with replaceState
|
|
2944
|
+
* use base app location.origin as baseURL
|
|
2945
|
+
*/
|
|
2946
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
2947
|
+
const targetLocation = createURL(to.path, rawLocation.origin);
|
|
2948
|
+
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
|
|
2949
|
+
if (getMicroPathFromURL(appName) !== targetFullPath) {
|
|
2950
|
+
navigateWithRawHistory(appName, to.replace === false ? 'pushState' : 'replaceState', targetLocation, to.state);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
else {
|
|
2955
|
+
logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`);
|
|
2956
|
+
}
|
|
2957
|
+
};
|
|
2958
|
+
}
|
|
2959
|
+
// create method of router.go/back/forward
|
|
2960
|
+
function createRawHistoryMethod(methodName) {
|
|
2961
|
+
return function (...rests) {
|
|
2962
|
+
return globalEnv.rawWindow.history[methodName](...rests);
|
|
2963
|
+
};
|
|
2964
|
+
}
|
|
2965
|
+
const beforeGuards = useSetRecord();
|
|
2966
|
+
const afterGuards = useSetRecord();
|
|
2967
|
+
/**
|
|
2968
|
+
* run all of beforeEach/afterEach guards
|
|
2969
|
+
* NOTE:
|
|
2970
|
+
* 1. Modify browser url first, and then run guards,
|
|
2971
|
+
* consistent with the browser forward & back button
|
|
2972
|
+
* 2. Note the element binding
|
|
2973
|
+
* @param appName app name
|
|
2974
|
+
* @param to target location
|
|
2975
|
+
* @param from old location
|
|
2976
|
+
* @param guards guards list
|
|
2977
|
+
*/
|
|
2978
|
+
function runGuards(appName, to, from, guards) {
|
|
2979
|
+
// clear element scope before execute function of parent
|
|
2980
|
+
removeDomScope();
|
|
2981
|
+
for (const guard of guards) {
|
|
2982
|
+
if (isFunction(guard)) {
|
|
2983
|
+
guard(appName, to, from);
|
|
2984
|
+
}
|
|
2985
|
+
else if (isPlainObject(guard) && isFunction(guard[appName])) {
|
|
2986
|
+
guard[appName](to, from);
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
/**
|
|
2991
|
+
* global hook for router
|
|
2992
|
+
* update router information base on microLocation
|
|
2993
|
+
* @param appName app name
|
|
2994
|
+
* @param microLocation location of microApp
|
|
2995
|
+
*/
|
|
2996
|
+
function executeNavigationGuard(appName, to, from) {
|
|
2997
|
+
router.current.set(appName, to);
|
|
2998
|
+
runGuards(appName, to, from, beforeGuards.list());
|
|
2999
|
+
requestIdleCallback(() => {
|
|
3000
|
+
runGuards(appName, to, from, afterGuards.list());
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
function clearRouterWhenUnmount(appName) {
|
|
3004
|
+
router.current.delete(appName);
|
|
3005
|
+
}
|
|
3006
|
+
// defaultPage data
|
|
3007
|
+
const defaultPageRecord = useMapRecord();
|
|
3008
|
+
/**
|
|
3009
|
+
* defaultPage只在子应用初始化时生效,且优先级比浏览器上的子应用路由地址低
|
|
3010
|
+
* @param appName app name
|
|
3011
|
+
* @param path page path
|
|
3012
|
+
*/
|
|
3013
|
+
function setDefaultPage(appName, path) {
|
|
3014
|
+
appName = formatAppName(appName);
|
|
3015
|
+
if (!appName)
|
|
3016
|
+
return noopFalse;
|
|
3017
|
+
return defaultPageRecord.add(appName, path);
|
|
3018
|
+
}
|
|
3019
|
+
function removeDefaultPage(appName) {
|
|
3020
|
+
appName = formatAppName(appName);
|
|
3021
|
+
if (!appName)
|
|
3022
|
+
return false;
|
|
3023
|
+
return defaultPageRecord.delete(appName);
|
|
3024
|
+
}
|
|
3025
|
+
// Router API for developer
|
|
3026
|
+
const router = {
|
|
3027
|
+
current: new Map(),
|
|
3028
|
+
encode: encodeMicroPath,
|
|
3029
|
+
decode: decodeMicroPath,
|
|
3030
|
+
push: createNavigationMethod(false),
|
|
3031
|
+
replace: createNavigationMethod(true),
|
|
3032
|
+
go: createRawHistoryMethod('go'),
|
|
3033
|
+
back: createRawHistoryMethod('back'),
|
|
3034
|
+
forward: createRawHistoryMethod('forward'),
|
|
3035
|
+
beforeEach: beforeGuards.add,
|
|
3036
|
+
afterEach: afterGuards.add,
|
|
3037
|
+
// attachToURL: 将指定的子应用路由信息添加到浏览器地址上
|
|
3038
|
+
// attachAllToURL: 将所有正在运行的子应用路由信息添加到浏览器地址上
|
|
3039
|
+
setDefaultPage,
|
|
3040
|
+
removeDefaultPage,
|
|
3041
|
+
getDefaultPage: defaultPageRecord.get,
|
|
3042
|
+
};
|
|
3043
|
+
return {
|
|
3044
|
+
router,
|
|
3045
|
+
executeNavigationGuard,
|
|
3046
|
+
clearRouterWhenUnmount,
|
|
3047
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
|
|
3050
|
+
|
|
3051
|
+
const shadowLocationKeys = ['href', 'pathname', 'search', 'hash'];
|
|
3052
|
+
// origin is readonly, so we ignore when updateMicroLocation
|
|
3053
|
+
const locationKeys = [...shadowLocationKeys, 'host', 'hostname', 'port', 'protocol', 'search'];
|
|
3054
|
+
// origin, fullPath is necessary for guardLocation
|
|
3055
|
+
const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
|
|
3056
|
+
/**
|
|
3057
|
+
* create guardLocation by microLocation, used for router guard
|
|
3058
|
+
*/
|
|
3059
|
+
function createGuardLocation(appName, microLocation) {
|
|
3060
|
+
const guardLocation = assign({ name: appName }, microLocation);
|
|
3061
|
+
// The prototype values on the URL needs to be manually transferred
|
|
3062
|
+
for (const key of guardLocationKeys)
|
|
3063
|
+
guardLocation[key] = microLocation[key];
|
|
3064
|
+
return guardLocation;
|
|
3065
|
+
}
|
|
3066
|
+
// for updateBrowserURLWithLocation when initial
|
|
3067
|
+
function autoTriggerNavigationGuard(appName, microLocation) {
|
|
3068
|
+
executeNavigationGuard(appName, createGuardLocation(appName, microLocation), createGuardLocation(appName, microLocation));
|
|
3069
|
+
}
|
|
3070
|
+
/**
|
|
3071
|
+
* The following scenes will trigger location update:
|
|
3072
|
+
* 1. pushState/replaceState
|
|
3073
|
+
* 2. popStateEvent
|
|
3074
|
+
* 3. query on browser url when init sub app
|
|
3075
|
+
* 4. set defaultPage when when init sub app
|
|
3076
|
+
* NOTE:
|
|
3077
|
+
* 1. update browser URL first, and then update microLocation
|
|
3078
|
+
* 2. the same fullPath will not trigger router guards
|
|
3079
|
+
* @param appName app name
|
|
3080
|
+
* @param path target path
|
|
3081
|
+
* @param base base url
|
|
3082
|
+
* @param microLocation micro app location
|
|
3083
|
+
* @param type auto prevent
|
|
3084
|
+
*/
|
|
3085
|
+
function updateMicroLocation(appName, path, microLocation, type) {
|
|
3086
|
+
const newLocation = createURL(path, microLocation.href);
|
|
3087
|
+
// record old values of microLocation to `from`
|
|
3088
|
+
const from = createGuardLocation(appName, microLocation);
|
|
3089
|
+
for (const key of locationKeys) {
|
|
3090
|
+
if (shadowLocationKeys.includes(key)) {
|
|
3091
|
+
// reference of shadowLocation
|
|
3092
|
+
microLocation.shadowLocation[key] = newLocation[key];
|
|
3093
|
+
}
|
|
3094
|
+
else {
|
|
3095
|
+
// @ts-ignore reference of microLocation
|
|
3096
|
+
microLocation[key] = newLocation[key];
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
// update latest values of microLocation to `to`
|
|
3100
|
+
const to = createGuardLocation(appName, microLocation);
|
|
3101
|
+
// The hook called only when fullPath changed
|
|
3102
|
+
if (type === 'auto' || (from.fullPath !== to.fullPath && type !== 'prevent')) {
|
|
3103
|
+
executeNavigationGuard(appName, to, from);
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
/**
|
|
3107
|
+
* Create location for microApp, each microApp has only one location object, it is a reference type
|
|
3108
|
+
* MDN https://developer.mozilla.org/en-US/docs/Web/API/Location
|
|
3109
|
+
* @param appName app name
|
|
3110
|
+
* @param url app url
|
|
3111
|
+
*/
|
|
3112
|
+
function createMicroLocation(appName, url) {
|
|
3113
|
+
const rawWindow = globalEnv.rawWindow;
|
|
3114
|
+
const rawLocation = rawWindow.location;
|
|
3115
|
+
// microLocation is the location of child app, it is globally unique
|
|
3116
|
+
const microLocation = createURL(url);
|
|
3117
|
+
// shadowLocation is the current location information (href, pathname, search, hash)
|
|
3118
|
+
const shadowLocation = {
|
|
3119
|
+
href: microLocation.href,
|
|
3120
|
+
pathname: microLocation.pathname,
|
|
3121
|
+
search: microLocation.search,
|
|
3122
|
+
hash: microLocation.hash,
|
|
3123
|
+
};
|
|
3124
|
+
/**
|
|
3125
|
+
* Common handler for href, assign, replace
|
|
3126
|
+
* It is mainly used to deal with special scenes about hash
|
|
3127
|
+
* @param value target path
|
|
3128
|
+
* @param methodName pushState/replaceState
|
|
3129
|
+
* @returns origin value or formatted value
|
|
3130
|
+
*/
|
|
3131
|
+
const commonHandler = (value, methodName) => {
|
|
3132
|
+
const targetLocation = createURL(value, microLocation.href);
|
|
3133
|
+
// Even if the origin is the same, developers still have the possibility of want to jump to a new page
|
|
3134
|
+
if (targetLocation.origin === microLocation.origin) {
|
|
3135
|
+
const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
|
|
3136
|
+
/**
|
|
3137
|
+
* change hash with location.href will not trigger the browser reload
|
|
3138
|
+
* so we use pushState & reload to imitate href behavior
|
|
3139
|
+
* NOTE:
|
|
3140
|
+
* 1. if child app only change hash, it should not trigger browser reload
|
|
3141
|
+
* 2. if address is same and has hash, it should not add route stack
|
|
3142
|
+
*/
|
|
3143
|
+
if (targetLocation.pathname === shadowLocation.pathname &&
|
|
3144
|
+
targetLocation.search === shadowLocation.search) {
|
|
3145
|
+
let oldHref = null;
|
|
3146
|
+
if (targetLocation.hash !== shadowLocation.hash) {
|
|
3147
|
+
if (setMicroPathResult.isAttach2Hash)
|
|
3148
|
+
oldHref = rawLocation.href;
|
|
3149
|
+
nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
|
|
3150
|
+
}
|
|
3151
|
+
if (targetLocation.hash) {
|
|
3152
|
+
dispatchNativeEvent(false, oldHref);
|
|
3153
|
+
}
|
|
3154
|
+
else {
|
|
3155
|
+
rawLocation.reload();
|
|
3156
|
+
}
|
|
3157
|
+
return void 0;
|
|
3158
|
+
/**
|
|
3159
|
+
* when baseApp is hash router, address change of child can not reload browser
|
|
3160
|
+
* so we imitate behavior of browser (reload)
|
|
3161
|
+
*/
|
|
3162
|
+
}
|
|
3163
|
+
else if (setMicroPathResult.isAttach2Hash) {
|
|
3164
|
+
nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
|
|
3165
|
+
rawLocation.reload();
|
|
3166
|
+
return void 0;
|
|
3167
|
+
}
|
|
3168
|
+
value = setMicroPathResult.fullPath;
|
|
3169
|
+
}
|
|
3170
|
+
return value;
|
|
3171
|
+
};
|
|
3172
|
+
/**
|
|
3173
|
+
* create location PropertyDescriptor (href, pathname, search, hash)
|
|
3174
|
+
* @param key property name
|
|
3175
|
+
* @param setter setter of location property
|
|
3176
|
+
*/
|
|
3177
|
+
function createPropertyDescriptor(getter, setter) {
|
|
3178
|
+
return {
|
|
3179
|
+
enumerable: true,
|
|
3180
|
+
configurable: true,
|
|
3181
|
+
get: getter,
|
|
3182
|
+
set: setter,
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
/**
|
|
3186
|
+
* common handler for location.pathname & location.search
|
|
3187
|
+
* @param targetPath target fullPath
|
|
3188
|
+
* @param key pathname/search
|
|
3189
|
+
*/
|
|
3190
|
+
function handleForPathNameAndSearch(targetPath, key) {
|
|
3191
|
+
const targetLocation = createURL(targetPath, url);
|
|
3192
|
+
// When the browser url has a hash value, the same pathname/search will not refresh browser
|
|
3193
|
+
if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
|
|
3194
|
+
// The href has not changed, not need to dispatch hashchange event
|
|
3195
|
+
dispatchNativeEvent(false);
|
|
3196
|
+
}
|
|
3197
|
+
else {
|
|
3198
|
+
/**
|
|
3199
|
+
* When the value is the same, no new route stack will be added
|
|
3200
|
+
* Special scenes such as:
|
|
3201
|
+
* pathname: /path ==> /path#hash, /path ==> /path?query
|
|
3202
|
+
* search: ?query ==> ?query#hash
|
|
3203
|
+
*/
|
|
3204
|
+
nativeHistoryNavigate(targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
|
|
3205
|
+
rawLocation.reload();
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
/**
|
|
3209
|
+
* Special processing for four keys: href, pathname, search and hash
|
|
3210
|
+
* They take values from shadowLocation, and require special operations when assigning values
|
|
3211
|
+
*/
|
|
3212
|
+
rawDefineProperties(microLocation, {
|
|
3213
|
+
href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
|
|
3214
|
+
const targetPath = commonHandler(value, 'pushState');
|
|
3215
|
+
if (targetPath)
|
|
3216
|
+
rawLocation.href = targetPath;
|
|
3217
|
+
}),
|
|
3218
|
+
pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
|
|
3219
|
+
const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
|
|
3220
|
+
handleForPathNameAndSearch(targetPath, 'pathname');
|
|
3221
|
+
}),
|
|
3222
|
+
search: createPropertyDescriptor(() => shadowLocation.search, (value) => {
|
|
3223
|
+
const targetPath = shadowLocation.pathname + ('?' + value).replace(/^\?+/, '?') + shadowLocation.hash;
|
|
3224
|
+
handleForPathNameAndSearch(targetPath, 'search');
|
|
3225
|
+
}),
|
|
3226
|
+
hash: createPropertyDescriptor(() => shadowLocation.hash, (value) => {
|
|
3227
|
+
const targetPath = shadowLocation.pathname + shadowLocation.search + ('#' + value).replace(/^#+/, '#');
|
|
3228
|
+
const targetLocation = createURL(targetPath, url);
|
|
3229
|
+
// The same hash will not trigger popStateEvent
|
|
3230
|
+
if (targetLocation.hash !== shadowLocation.hash) {
|
|
3231
|
+
navigateWithNativeEvent('pushState', setMicroPathToURL(appName, targetLocation), false);
|
|
3232
|
+
}
|
|
3233
|
+
}),
|
|
3234
|
+
fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
|
|
3235
|
+
});
|
|
3236
|
+
const createLocationMethod = (locationMethodName) => {
|
|
3237
|
+
return function (value) {
|
|
3238
|
+
const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
|
|
3239
|
+
if (targetPath)
|
|
3240
|
+
rawLocation[locationMethodName](targetPath);
|
|
3241
|
+
};
|
|
3242
|
+
};
|
|
3243
|
+
return assign(microLocation, {
|
|
3244
|
+
assign: createLocationMethod('assign'),
|
|
3245
|
+
replace: createLocationMethod('replace'),
|
|
3246
|
+
reload: (forcedReload) => rawLocation.reload(forcedReload),
|
|
3247
|
+
shadowLocation,
|
|
3248
|
+
});
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
/**
|
|
3252
|
+
* The router system has two operations: read and write
|
|
3253
|
+
* Read through location and write through history & location
|
|
3254
|
+
* @param appName app name
|
|
3255
|
+
* @param url app url
|
|
3256
|
+
* @returns MicroRouter
|
|
3257
|
+
*/
|
|
3258
|
+
function createMicroRouter(appName, url) {
|
|
3259
|
+
rewriteHistoryState();
|
|
3260
|
+
const microLocation = createMicroLocation(appName, url);
|
|
3261
|
+
return {
|
|
3262
|
+
microLocation,
|
|
3263
|
+
microHistory: createMicroHistory(appName, microLocation),
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
// 当沙箱执行start, 或者隐藏的keep-alive应用重新渲染时时才根据浏览器url更新location 或者 将参数更新到url上
|
|
3267
|
+
function initRouteStateWithURL(appName, microLocation, defaultPage) {
|
|
3268
|
+
const microPath = getMicroPathFromURL(appName);
|
|
3269
|
+
if (microPath) {
|
|
3270
|
+
updateMicroLocation(appName, microPath, microLocation, 'auto');
|
|
3271
|
+
}
|
|
3272
|
+
else {
|
|
3273
|
+
updateBrowserURLWithLocation(appName, microLocation, defaultPage);
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
/**
|
|
3277
|
+
* initialize browser information according to microLocation
|
|
3278
|
+
* called on sandbox.start or reshow of keep-alive app
|
|
3279
|
+
*/
|
|
3280
|
+
function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
|
|
3281
|
+
// update microLocation with defaultPage
|
|
3282
|
+
if (defaultPage)
|
|
3283
|
+
updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
|
|
3284
|
+
// attach microApp route info to browser URL
|
|
3285
|
+
updateBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, globalEnv.rawWindow.history.state, null));
|
|
3286
|
+
// trigger guards after change browser URL
|
|
3287
|
+
autoTriggerNavigationGuard(appName, microLocation);
|
|
3288
|
+
}
|
|
3289
|
+
/**
|
|
3290
|
+
* In any case, microPath & microState will be removed from browser, but location will be initialized only when keep-router-state is false
|
|
3291
|
+
* @param appName app name
|
|
3292
|
+
* @param url app url
|
|
3293
|
+
* @param microLocation location of microApp
|
|
3294
|
+
* @param keepRouteState keep-router-state is only used to control whether to clear the location of microApp
|
|
3295
|
+
*/
|
|
3296
|
+
function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
|
|
3297
|
+
if (!keepRouteState) {
|
|
3298
|
+
const { pathname, search, hash } = createURL(url);
|
|
3299
|
+
updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
|
|
3300
|
+
}
|
|
3301
|
+
removeStateAndPathFromBrowser(appName);
|
|
3302
|
+
clearRouterWhenUnmount(appName);
|
|
3303
|
+
}
|
|
3304
|
+
/**
|
|
3305
|
+
* remove microState from history.state and remove microPath from browserURL
|
|
3306
|
+
* called on sandbox.stop or hidden of keep-alive app
|
|
3307
|
+
*/
|
|
3308
|
+
function removeStateAndPathFromBrowser(appName) {
|
|
3309
|
+
updateBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
// Variables that can escape to rawWindow
|
|
3313
|
+
const staticEscapeProperties = [
|
|
3314
|
+
'__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
|
|
3315
|
+
'System',
|
|
3316
|
+
'__cjsWrapper',
|
|
3317
|
+
];
|
|
3318
|
+
// Variables that can only assigned to rawWindow
|
|
3319
|
+
const escapeSetterKeyList = [
|
|
3320
|
+
'location',
|
|
3321
|
+
];
|
|
3322
|
+
const globalPropertyList = ['window', 'self', 'globalThis'];
|
|
3323
|
+
class SandBox {
|
|
3324
|
+
constructor(appName, url, useMemoryRouter = true) {
|
|
3325
|
+
/**
|
|
3326
|
+
* Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
|
|
3327
|
+
* https://github.com/micro-zoe/micro-app/issues/234
|
|
3328
|
+
*/
|
|
3329
|
+
this.scopeProperties = ['webpackJsonp', 'Vue'];
|
|
3330
|
+
// Properties that can be escape to rawWindow
|
|
3331
|
+
this.escapeProperties = [];
|
|
3332
|
+
// Properties newly added to microAppWindow
|
|
3333
|
+
this.injectedKeys = new Set();
|
|
3334
|
+
// Properties escape to rawWindow, cleared when unmount
|
|
3335
|
+
this.escapeKeys = new Set();
|
|
3336
|
+
// sandbox state
|
|
3337
|
+
this.active = false;
|
|
3338
|
+
this.microAppWindow = {}; // Proxy target
|
|
3339
|
+
// get scopeProperties and escapeProperties from plugins
|
|
3340
|
+
this.getSpecialProperties(appName);
|
|
3341
|
+
// create proxyWindow with Proxy(microAppWindow)
|
|
3342
|
+
this.proxyWindow = this.createProxyWindow(appName);
|
|
3343
|
+
// inject global properties
|
|
3344
|
+
this.initMicroAppWindow(this.microAppWindow, appName, url, useMemoryRouter);
|
|
3345
|
+
// Rewrite global event listener & timeout
|
|
3346
|
+
assign(this, effect(this.microAppWindow));
|
|
3347
|
+
}
|
|
3348
|
+
start(baseRoute, useMemoryRouter = true, defaultPage = '') {
|
|
3349
|
+
if (!this.active) {
|
|
3350
|
+
this.active = true;
|
|
3351
|
+
if (useMemoryRouter) {
|
|
3352
|
+
this.initRouteState(defaultPage);
|
|
3353
|
+
// unique listener of popstate event for sub app
|
|
3354
|
+
this.removeHistoryListener = addHistoryListener(this.proxyWindow.__MICRO_APP_NAME__);
|
|
3355
|
+
}
|
|
3356
|
+
else {
|
|
3357
|
+
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
|
|
3358
|
+
}
|
|
3359
|
+
if (++SandBox.activeCount === 1) {
|
|
3360
|
+
effectDocumentEvent();
|
|
3361
|
+
patchElementPrototypeMethods();
|
|
3362
|
+
listenUmountOfNestedApp();
|
|
3363
|
+
}
|
|
3364
|
+
// BUG FIX: bable-polyfill@6.x
|
|
3365
|
+
globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
stop(keepRouteState = false) {
|
|
3369
|
+
if (this.active) {
|
|
3370
|
+
this.releaseEffect();
|
|
3371
|
+
this.microAppWindow.microApp.clearDataListener();
|
|
3372
|
+
this.microAppWindow.microApp.clearGlobalDataListener();
|
|
3373
|
+
this.injectedKeys.forEach((key) => {
|
|
3374
|
+
Reflect.deleteProperty(this.microAppWindow, key);
|
|
3375
|
+
});
|
|
3376
|
+
this.injectedKeys.clear();
|
|
3377
|
+
this.escapeKeys.forEach((key) => {
|
|
3378
|
+
Reflect.deleteProperty(globalEnv.rawWindow, key);
|
|
3379
|
+
});
|
|
3380
|
+
this.escapeKeys.clear();
|
|
3381
|
+
if (this.removeHistoryListener) {
|
|
3382
|
+
this.clearRouteState(keepRouteState);
|
|
3383
|
+
// release listener of popstate
|
|
3384
|
+
this.removeHistoryListener();
|
|
3385
|
+
}
|
|
3386
|
+
if (--SandBox.activeCount === 0) {
|
|
2599
3387
|
releaseEffectDocumentEvent();
|
|
2600
3388
|
releasePatches();
|
|
2601
3389
|
}
|
|
3390
|
+
this.active = false;
|
|
2602
3391
|
}
|
|
2603
3392
|
}
|
|
2604
3393
|
// record umd snapshot before the first execution of umdHookMount
|
|
@@ -2743,20 +3532,25 @@ class SandBox {
|
|
|
2743
3532
|
* @param appName app name
|
|
2744
3533
|
* @param url app url
|
|
2745
3534
|
*/
|
|
2746
|
-
initMicroAppWindow(microAppWindow, appName, url) {
|
|
3535
|
+
initMicroAppWindow(microAppWindow, appName, url, useMemoryRouter) {
|
|
2747
3536
|
microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
|
|
2748
3537
|
microAppWindow.__MICRO_APP_NAME__ = appName;
|
|
3538
|
+
microAppWindow.__MICRO_APP_URL__ = url;
|
|
2749
3539
|
microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
|
|
2750
3540
|
microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
|
|
2751
|
-
microAppWindow.microApp =
|
|
3541
|
+
microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
|
|
2752
3542
|
removeDomScope,
|
|
2753
3543
|
pureCreateElement,
|
|
3544
|
+
router,
|
|
2754
3545
|
});
|
|
2755
3546
|
microAppWindow.rawWindow = globalEnv.rawWindow;
|
|
2756
3547
|
microAppWindow.rawDocument = globalEnv.rawDocument;
|
|
2757
3548
|
microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
|
|
2758
3549
|
this.setMappingPropertiesWithRawDescriptor(microAppWindow);
|
|
2759
3550
|
this.setHijackProperties(microAppWindow, appName);
|
|
3551
|
+
// this.patchHijackRequest(microAppWindow, appName, url)
|
|
3552
|
+
if (useMemoryRouter)
|
|
3553
|
+
this.setRouterApi(microAppWindow, appName, url);
|
|
2760
3554
|
}
|
|
2761
3555
|
// properties associated with the native window
|
|
2762
3556
|
setMappingPropertiesWithRawDescriptor(microAppWindow) {
|
|
@@ -2790,14 +3584,16 @@ class SandBox {
|
|
|
2790
3584
|
let modifiedEval, modifiedImage;
|
|
2791
3585
|
rawDefineProperties(microAppWindow, {
|
|
2792
3586
|
document: {
|
|
3587
|
+
configurable: false,
|
|
3588
|
+
enumerable: true,
|
|
2793
3589
|
get() {
|
|
2794
3590
|
throttleDeferForSetAppName(appName);
|
|
2795
3591
|
return globalEnv.rawDocument;
|
|
2796
3592
|
},
|
|
2797
|
-
configurable: false,
|
|
2798
|
-
enumerable: true,
|
|
2799
3593
|
},
|
|
2800
3594
|
eval: {
|
|
3595
|
+
configurable: true,
|
|
3596
|
+
enumerable: false,
|
|
2801
3597
|
get() {
|
|
2802
3598
|
throttleDeferForSetAppName(appName);
|
|
2803
3599
|
return modifiedEval || eval;
|
|
@@ -2805,10 +3601,10 @@ class SandBox {
|
|
|
2805
3601
|
set: (value) => {
|
|
2806
3602
|
modifiedEval = value;
|
|
2807
3603
|
},
|
|
2808
|
-
configurable: true,
|
|
2809
|
-
enumerable: false,
|
|
2810
3604
|
},
|
|
2811
3605
|
Image: {
|
|
3606
|
+
configurable: true,
|
|
3607
|
+
enumerable: false,
|
|
2812
3608
|
get() {
|
|
2813
3609
|
throttleDeferForSetAppName(appName);
|
|
2814
3610
|
return modifiedImage || globalEnv.ImageProxy;
|
|
@@ -2816,11 +3612,101 @@ class SandBox {
|
|
|
2816
3612
|
set: (value) => {
|
|
2817
3613
|
modifiedImage = value;
|
|
2818
3614
|
},
|
|
3615
|
+
},
|
|
3616
|
+
});
|
|
3617
|
+
}
|
|
3618
|
+
// private patchHijackRequest (microAppWindow: microAppWindowType, appName: string, url: string) {
|
|
3619
|
+
// let modifiedImage: unknown
|
|
3620
|
+
// function EventSource (...rests: any[]) {
|
|
3621
|
+
// console.log(appName + ' EventSource', rests)
|
|
3622
|
+
// if (typeof rests[0] === 'string') {
|
|
3623
|
+
// rests[0] = (new URL(rests[0], url)).toString()
|
|
3624
|
+
// }
|
|
3625
|
+
// return new globalEnv.rawWindow.EventSource(...rests)
|
|
3626
|
+
// }
|
|
3627
|
+
// function patchFetch (...rests: any[]) {
|
|
3628
|
+
// console.log(appName + ' fetch', rests)
|
|
3629
|
+
// if (typeof rests[0] === 'string') {
|
|
3630
|
+
// rests[0] = (new URL(rests[0], url)).toString()
|
|
3631
|
+
// }
|
|
3632
|
+
// return globalEnv.rawWindow.fetch(...rests)
|
|
3633
|
+
// }
|
|
3634
|
+
// const rawXMLHttpRequest = globalEnv.rawWindow.XMLHttpRequest
|
|
3635
|
+
// class XMLHttpRequest extends rawXMLHttpRequest {
|
|
3636
|
+
// open (method: string, reqUrl: string) {
|
|
3637
|
+
// console.log(appName + ' XMLHttpRequest', method, reqUrl)
|
|
3638
|
+
// reqUrl = (new URL(reqUrl, url)).toString()
|
|
3639
|
+
// super.open(method, reqUrl)
|
|
3640
|
+
// }
|
|
3641
|
+
// }
|
|
3642
|
+
// rawDefineProperties(microAppWindow, {
|
|
3643
|
+
// EventSource: {
|
|
3644
|
+
// configurable: true,
|
|
3645
|
+
// enumerable: true,
|
|
3646
|
+
// get () {
|
|
3647
|
+
// return EventSource
|
|
3648
|
+
// },
|
|
3649
|
+
// set: (value) => {
|
|
3650
|
+
// modifiedImage = value
|
|
3651
|
+
// },
|
|
3652
|
+
// },
|
|
3653
|
+
// fetch: {
|
|
3654
|
+
// configurable: true,
|
|
3655
|
+
// enumerable: true,
|
|
3656
|
+
// get () {
|
|
3657
|
+
// return patchFetch
|
|
3658
|
+
// },
|
|
3659
|
+
// set: (value) => {
|
|
3660
|
+
// modifiedImage = value
|
|
3661
|
+
// },
|
|
3662
|
+
// },
|
|
3663
|
+
// XMLHttpRequest: {
|
|
3664
|
+
// configurable: true,
|
|
3665
|
+
// enumerable: true,
|
|
3666
|
+
// get () {
|
|
3667
|
+
// return XMLHttpRequest
|
|
3668
|
+
// },
|
|
3669
|
+
// set: (value) => {
|
|
3670
|
+
// modifiedImage = value
|
|
3671
|
+
// },
|
|
3672
|
+
// },
|
|
3673
|
+
// })
|
|
3674
|
+
// }
|
|
3675
|
+
// set location & history for memory router
|
|
3676
|
+
setRouterApi(microAppWindow, appName, url) {
|
|
3677
|
+
const { microLocation, microHistory } = createMicroRouter(appName, url);
|
|
3678
|
+
rawDefineProperties(microAppWindow, {
|
|
3679
|
+
location: {
|
|
3680
|
+
configurable: false,
|
|
3681
|
+
enumerable: true,
|
|
3682
|
+
get() {
|
|
3683
|
+
return microLocation;
|
|
3684
|
+
},
|
|
3685
|
+
set: (value) => {
|
|
3686
|
+
globalEnv.rawWindow.location = value;
|
|
3687
|
+
},
|
|
3688
|
+
},
|
|
3689
|
+
history: {
|
|
2819
3690
|
configurable: true,
|
|
2820
|
-
enumerable:
|
|
3691
|
+
enumerable: true,
|
|
3692
|
+
get() {
|
|
3693
|
+
return microHistory;
|
|
3694
|
+
},
|
|
2821
3695
|
},
|
|
2822
3696
|
});
|
|
2823
3697
|
}
|
|
3698
|
+
initRouteState(defaultPage) {
|
|
3699
|
+
initRouteStateWithURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location, defaultPage);
|
|
3700
|
+
}
|
|
3701
|
+
clearRouteState(keepRouteState) {
|
|
3702
|
+
clearRouteStateFromURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, this.proxyWindow.location, keepRouteState);
|
|
3703
|
+
}
|
|
3704
|
+
setRouteInfoForKeepAliveApp() {
|
|
3705
|
+
updateBrowserURLWithLocation(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location);
|
|
3706
|
+
}
|
|
3707
|
+
removeRouteInfoForKeepAliveApp() {
|
|
3708
|
+
removeStateAndPathFromBrowser(this.proxyWindow.__MICRO_APP_NAME__);
|
|
3709
|
+
}
|
|
2824
3710
|
}
|
|
2825
3711
|
SandBox.activeCount = 0; // number of active sandbox
|
|
2826
3712
|
|
|
@@ -2854,7 +3740,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
|
|
|
2854
3740
|
element = getRootContainer(element);
|
|
2855
3741
|
// clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
|
|
2856
3742
|
removeDomScope();
|
|
2857
|
-
const detail =
|
|
3743
|
+
const detail = assign({
|
|
2858
3744
|
name: appName,
|
|
2859
3745
|
container: element,
|
|
2860
3746
|
}, error && {
|
|
@@ -2879,7 +3765,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
|
|
|
2879
3765
|
* @param detail event detail
|
|
2880
3766
|
*/
|
|
2881
3767
|
function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
|
|
2882
|
-
const event = new CustomEvent(
|
|
3768
|
+
const event = new CustomEvent(formatEventName$1(eventName, appName), {
|
|
2883
3769
|
detail,
|
|
2884
3770
|
});
|
|
2885
3771
|
window.dispatchEvent(event);
|
|
@@ -2888,8 +3774,8 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
|
|
|
2888
3774
|
// micro app instances
|
|
2889
3775
|
const appInstanceMap = new Map();
|
|
2890
3776
|
class CreateApp {
|
|
2891
|
-
constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, baseroute, }) {
|
|
2892
|
-
this.state = appStates.
|
|
3777
|
+
constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, defaultPage, }) {
|
|
3778
|
+
this.state = appStates.CREATED;
|
|
2893
3779
|
this.keepAliveState = null;
|
|
2894
3780
|
this.keepAliveContainer = null;
|
|
2895
3781
|
this.loadSourceLevel = 0;
|
|
@@ -2900,28 +3786,30 @@ class CreateApp {
|
|
|
2900
3786
|
this.isPrefetch = false;
|
|
2901
3787
|
this.prefetchResolve = null;
|
|
2902
3788
|
this.container = null;
|
|
2903
|
-
this.baseroute = '';
|
|
2904
3789
|
this.sandBox = null;
|
|
2905
3790
|
this.container = container !== null && container !== void 0 ? container : null;
|
|
2906
3791
|
this.inline = inline !== null && inline !== void 0 ? inline : false;
|
|
2907
3792
|
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
|
|
3793
|
+
this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : false;
|
|
2908
3794
|
this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
|
|
2909
3795
|
// optional during init👆
|
|
2910
3796
|
this.name = name;
|
|
2911
3797
|
this.url = url;
|
|
2912
3798
|
this.useSandbox = useSandbox;
|
|
2913
3799
|
this.scopecss = this.useSandbox && scopecss;
|
|
3800
|
+
this.useMemoryRouter = this.useSandbox && useMemoryRouter;
|
|
3801
|
+
this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : '';
|
|
2914
3802
|
this.source = {
|
|
2915
3803
|
links: new Map(),
|
|
2916
3804
|
scripts: new Map(),
|
|
2917
3805
|
};
|
|
2918
3806
|
this.loadSourceCode();
|
|
2919
|
-
this.useSandbox && (this.sandBox = new SandBox(name, url));
|
|
3807
|
+
this.useSandbox && (this.sandBox = new SandBox(name, url, this.useMemoryRouter));
|
|
2920
3808
|
}
|
|
2921
3809
|
// Load resources
|
|
2922
3810
|
loadSourceCode() {
|
|
2923
|
-
this.state = appStates.
|
|
2924
|
-
|
|
3811
|
+
this.state = appStates.LOADING;
|
|
3812
|
+
extractHtml(this);
|
|
2925
3813
|
}
|
|
2926
3814
|
/**
|
|
2927
3815
|
* When resource is loaded, mount app if it is not prefetch or unmount
|
|
@@ -2935,7 +3823,7 @@ class CreateApp {
|
|
|
2935
3823
|
this.prefetchResolve = null;
|
|
2936
3824
|
}
|
|
2937
3825
|
else if (appStates.UNMOUNT !== this.state) {
|
|
2938
|
-
this.state = appStates.
|
|
3826
|
+
this.state = appStates.LOADED;
|
|
2939
3827
|
this.mount();
|
|
2940
3828
|
}
|
|
2941
3829
|
}
|
|
@@ -2952,7 +3840,7 @@ class CreateApp {
|
|
|
2952
3840
|
}
|
|
2953
3841
|
if (appStates.UNMOUNT !== this.state) {
|
|
2954
3842
|
this.onerror(e);
|
|
2955
|
-
this.state = appStates.
|
|
3843
|
+
this.state = appStates.LOAD_FAILED;
|
|
2956
3844
|
}
|
|
2957
3845
|
}
|
|
2958
3846
|
/**
|
|
@@ -2960,22 +3848,26 @@ class CreateApp {
|
|
|
2960
3848
|
* @param container app container
|
|
2961
3849
|
* @param inline js runs in inline mode
|
|
2962
3850
|
* @param baseroute route prefix, default is ''
|
|
3851
|
+
* @param keepRouteState keep route state when unmount, default is false
|
|
2963
3852
|
*/
|
|
2964
|
-
mount(container, inline, baseroute) {
|
|
3853
|
+
mount(container, inline, baseroute, keepRouteState, defaultPage) {
|
|
2965
3854
|
var _a, _b, _c;
|
|
2966
|
-
if (isBoolean(inline)
|
|
3855
|
+
if (isBoolean(inline))
|
|
2967
3856
|
this.inline = inline;
|
|
2968
|
-
|
|
3857
|
+
// keepRouteState effective on unmount
|
|
3858
|
+
if (isBoolean(keepRouteState))
|
|
3859
|
+
this.keepRouteState = keepRouteState;
|
|
2969
3860
|
this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
|
|
2970
3861
|
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
|
|
3862
|
+
this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : this.defaultPage;
|
|
2971
3863
|
if (this.loadSourceLevel !== 2) {
|
|
2972
|
-
this.state = appStates.
|
|
3864
|
+
this.state = appStates.LOADING;
|
|
2973
3865
|
return;
|
|
2974
3866
|
}
|
|
2975
3867
|
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
|
|
2976
3868
|
this.state = appStates.MOUNTING;
|
|
2977
3869
|
cloneContainer(this.source.html, this.container, !this.umdMode);
|
|
2978
|
-
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
|
|
3870
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute, this.useMemoryRouter, this.defaultPage);
|
|
2979
3871
|
let umdHookMountResult; // result of mount function
|
|
2980
3872
|
if (!this.umdMode) {
|
|
2981
3873
|
let hasDispatchMountedEvent = false;
|
|
@@ -3040,11 +3932,12 @@ class CreateApp {
|
|
|
3040
3932
|
}
|
|
3041
3933
|
/**
|
|
3042
3934
|
* unmount app
|
|
3935
|
+
* NOTE: Do not add any params on account of unmountApp
|
|
3043
3936
|
* @param destroy completely destroy, delete cache resources
|
|
3044
3937
|
* @param unmountcb callback of unmount
|
|
3045
3938
|
*/
|
|
3046
3939
|
unmount(destroy, unmountcb) {
|
|
3047
|
-
if (this.state === appStates.
|
|
3940
|
+
if (this.state === appStates.LOAD_FAILED) {
|
|
3048
3941
|
destroy = true;
|
|
3049
3942
|
}
|
|
3050
3943
|
this.state = appStates.UNMOUNT;
|
|
@@ -3098,7 +3991,7 @@ class CreateApp {
|
|
|
3098
3991
|
cloneContainer(this.container, this.source.html, false);
|
|
3099
3992
|
}
|
|
3100
3993
|
// this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
|
|
3101
|
-
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
|
|
3994
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop(this.keepRouteState && !destroy);
|
|
3102
3995
|
if (!getActiveApps().length) {
|
|
3103
3996
|
releasePatchSetAttribute();
|
|
3104
3997
|
}
|
|
@@ -3117,34 +4010,40 @@ class CreateApp {
|
|
|
3117
4010
|
}
|
|
3118
4011
|
// hidden app when disconnectedCallback called with keep-alive
|
|
3119
4012
|
hiddenKeepAliveApp() {
|
|
4013
|
+
var _a;
|
|
3120
4014
|
const oldContainer = this.container;
|
|
3121
4015
|
cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
|
|
3122
4016
|
this.container = this.keepAliveContainer;
|
|
3123
4017
|
this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
|
|
3124
4018
|
// event should dispatch before clone node
|
|
3125
|
-
// dispatch
|
|
4019
|
+
// dispatch afterHidden event to micro-app
|
|
3126
4020
|
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
3127
4021
|
appState: 'afterhidden',
|
|
3128
4022
|
});
|
|
3129
|
-
// dispatch
|
|
4023
|
+
// dispatch afterHidden event to base app
|
|
3130
4024
|
dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
|
|
4025
|
+
// called after lifeCyclesEvent
|
|
4026
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
|
|
3131
4027
|
}
|
|
3132
4028
|
// show app when connectedCallback called with keep-alive
|
|
3133
4029
|
showKeepAliveApp(container) {
|
|
3134
|
-
|
|
4030
|
+
var _a;
|
|
4031
|
+
// dispatch beforeShow event to micro-app
|
|
3135
4032
|
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
3136
4033
|
appState: 'beforeshow',
|
|
3137
4034
|
});
|
|
3138
|
-
// dispatch
|
|
4035
|
+
// dispatch beforeShow event to base app
|
|
3139
4036
|
dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
|
|
3140
4037
|
cloneContainer(this.container, container, false);
|
|
3141
4038
|
this.container = container;
|
|
3142
4039
|
this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
|
|
3143
|
-
//
|
|
4040
|
+
// called before lifeCyclesEvent
|
|
4041
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setRouteInfoForKeepAliveApp();
|
|
4042
|
+
// dispatch afterShow event to micro-app
|
|
3144
4043
|
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
3145
4044
|
appState: 'aftershow',
|
|
3146
4045
|
});
|
|
3147
|
-
// dispatch
|
|
4046
|
+
// dispatch afterShow event to base app
|
|
3148
4047
|
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
|
|
3149
4048
|
}
|
|
3150
4049
|
/**
|
|
@@ -3252,12 +4151,17 @@ function defineElement(tagName) {
|
|
|
3252
4151
|
}
|
|
3253
4152
|
disconnectedCallback() {
|
|
3254
4153
|
this.hasConnected = false;
|
|
3255
|
-
|
|
3256
|
-
if (
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
4154
|
+
const app = appInstanceMap.get(this.appName);
|
|
4155
|
+
if (app &&
|
|
4156
|
+
app.getAppState() !== appStates.UNMOUNT &&
|
|
4157
|
+
app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
|
|
4158
|
+
// keep-alive
|
|
4159
|
+
if (this.getKeepAliveModeResult()) {
|
|
4160
|
+
this.handleHiddenKeepAliveApp();
|
|
4161
|
+
}
|
|
4162
|
+
else {
|
|
4163
|
+
this.handleUnmount(this.getDestroyCompatibleResult());
|
|
4164
|
+
}
|
|
3261
4165
|
}
|
|
3262
4166
|
}
|
|
3263
4167
|
attributeChangedCallback(attr, _oldVal, newVal) {
|
|
@@ -3305,12 +4209,7 @@ function defineElement(tagName) {
|
|
|
3305
4209
|
if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
|
|
3306
4210
|
this.attachShadow({ mode: 'open' });
|
|
3307
4211
|
}
|
|
3308
|
-
|
|
3309
|
-
this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, this.appUrl);
|
|
3310
|
-
}
|
|
3311
|
-
else if (this.ssrUrl) {
|
|
3312
|
-
this.ssrUrl = '';
|
|
3313
|
-
}
|
|
4212
|
+
this.updateSsrUrl(this.appUrl);
|
|
3314
4213
|
if (appInstanceMap.has(this.appName)) {
|
|
3315
4214
|
const app = appInstanceMap.get(this.appName);
|
|
3316
4215
|
const existAppUrl = app.ssrUrl || app.url;
|
|
@@ -3344,15 +4243,9 @@ function defineElement(tagName) {
|
|
|
3344
4243
|
actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp) {
|
|
3345
4244
|
var _a;
|
|
3346
4245
|
/**
|
|
3347
|
-
* change ssrUrl in ssr mode
|
|
3348
4246
|
* do not add judgment of formatAttrUrl === this.appUrl
|
|
3349
4247
|
*/
|
|
3350
|
-
|
|
3351
|
-
this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
|
|
3352
|
-
}
|
|
3353
|
-
else if (this.ssrUrl) {
|
|
3354
|
-
this.ssrUrl = '';
|
|
3355
|
-
}
|
|
4248
|
+
this.updateSsrUrl(formatAttrUrl);
|
|
3356
4249
|
this.appName = formatAttrName;
|
|
3357
4250
|
this.appUrl = formatAttrUrl;
|
|
3358
4251
|
((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
|
|
@@ -3412,14 +4305,14 @@ function defineElement(tagName) {
|
|
|
3412
4305
|
app.isPrefetch = false;
|
|
3413
4306
|
defer(() => {
|
|
3414
4307
|
var _a;
|
|
3415
|
-
return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
|
|
4308
|
+
return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible(), this.getDisposeResult('keep-router-state'), this.getDefaultPageValue());
|
|
3416
4309
|
});
|
|
3417
4310
|
}
|
|
3418
4311
|
// create app instance
|
|
3419
4312
|
handleCreateApp() {
|
|
3420
4313
|
var _a;
|
|
3421
4314
|
/**
|
|
3422
|
-
* actions for
|
|
4315
|
+
* actions for destroy old app
|
|
3423
4316
|
* fix of unmounted umd app with disableSandbox
|
|
3424
4317
|
*/
|
|
3425
4318
|
if (appInstanceMap.has(this.appName)) {
|
|
@@ -3433,7 +4326,10 @@ function defineElement(tagName) {
|
|
|
3433
4326
|
inline: this.getDisposeResult('inline'),
|
|
3434
4327
|
scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
|
|
3435
4328
|
useSandbox: !this.getDisposeResult('disableSandbox'),
|
|
4329
|
+
useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
|
|
3436
4330
|
baseroute: this.getBaseRouteCompatible(),
|
|
4331
|
+
keepRouteState: this.getDisposeResult('keep-router-state'),
|
|
4332
|
+
defaultPage: this.getDefaultPageValue(),
|
|
3437
4333
|
});
|
|
3438
4334
|
appInstanceMap.set(this.appName, instance);
|
|
3439
4335
|
}
|
|
@@ -3441,11 +4337,12 @@ function defineElement(tagName) {
|
|
|
3441
4337
|
* unmount app
|
|
3442
4338
|
* @param destroy delete cache resources when unmount
|
|
3443
4339
|
*/
|
|
3444
|
-
handleUnmount(destroy,
|
|
4340
|
+
handleUnmount(destroy, unmountCb) {
|
|
3445
4341
|
const app = appInstanceMap.get(this.appName);
|
|
3446
4342
|
if (app &&
|
|
3447
|
-
app.getAppState() !== appStates.UNMOUNT)
|
|
3448
|
-
app.unmount(destroy,
|
|
4343
|
+
app.getAppState() !== appStates.UNMOUNT) {
|
|
4344
|
+
app.unmount(destroy, unmountCb);
|
|
4345
|
+
}
|
|
3449
4346
|
}
|
|
3450
4347
|
// hidden app when disconnectedCallback called with keep-alive
|
|
3451
4348
|
handleHiddenKeepAliveApp() {
|
|
@@ -3508,6 +4405,37 @@ function defineElement(tagName) {
|
|
|
3508
4405
|
getKeepAliveModeResult() {
|
|
3509
4406
|
return this.getDisposeResult('keep-alive') && !this.getDestroyCompatibleResult();
|
|
3510
4407
|
}
|
|
4408
|
+
/**
|
|
4409
|
+
* change ssrUrl in ssr mode
|
|
4410
|
+
*/
|
|
4411
|
+
updateSsrUrl(baseUrl) {
|
|
4412
|
+
if (this.getDisposeResult('ssr')) {
|
|
4413
|
+
if (this.getDisposeResult('disable-memory-router')) {
|
|
4414
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
4415
|
+
this.ssrUrl = CompletionPath(rawLocation.pathname + rawLocation.search, baseUrl);
|
|
4416
|
+
}
|
|
4417
|
+
else {
|
|
4418
|
+
// get path from browser URL
|
|
4419
|
+
let targetPath = getNoHashMicroPathFromURL(this.appName, baseUrl);
|
|
4420
|
+
const defaultPagePath = this.getDefaultPageValue();
|
|
4421
|
+
if (!targetPath && defaultPagePath) {
|
|
4422
|
+
const targetLocation = createURL(defaultPagePath, baseUrl);
|
|
4423
|
+
targetPath = targetLocation.origin + targetLocation.pathname + targetLocation.search;
|
|
4424
|
+
}
|
|
4425
|
+
this.ssrUrl = targetPath;
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
else if (this.ssrUrl) {
|
|
4429
|
+
this.ssrUrl = '';
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
/**
|
|
4433
|
+
* get config of default page
|
|
4434
|
+
*/
|
|
4435
|
+
getDefaultPageValue() {
|
|
4436
|
+
var _a, _b, _c;
|
|
4437
|
+
return (_c = (_b = (_a = router.getDefaultPage(this.appName)) !== null && _a !== void 0 ? _a : this.getAttribute('default-page')) !== null && _b !== void 0 ? _b : this.getAttribute('defaultPage')) !== null && _c !== void 0 ? _c : '';
|
|
4438
|
+
}
|
|
3511
4439
|
/**
|
|
3512
4440
|
* Data from the base application
|
|
3513
4441
|
*/
|
|
@@ -3542,12 +4470,13 @@ function defineElement(tagName) {
|
|
|
3542
4470
|
* url: string,
|
|
3543
4471
|
* disableScopecss?: boolean,
|
|
3544
4472
|
* disableSandbox?: boolean,
|
|
4473
|
+
* disableMemoryRouter?: boolean,
|
|
3545
4474
|
* },
|
|
3546
4475
|
* ...
|
|
3547
4476
|
* ])
|
|
3548
4477
|
* Note:
|
|
3549
4478
|
* 1: preFetch is asynchronous and is performed only when the browser is idle
|
|
3550
|
-
* 2: disableScopecss, disableSandbox must be same with micro-app element, if conflict, the one who executes first shall prevail
|
|
4479
|
+
* 2: disableScopecss, disableSandbox, disableMemoryRouter must be same with micro-app element, if conflict, the one who executes first shall prevail
|
|
3551
4480
|
* @param apps micro apps
|
|
3552
4481
|
*/
|
|
3553
4482
|
function preFetch(apps) {
|
|
@@ -3565,7 +4494,7 @@ function preFetch(apps) {
|
|
|
3565
4494
|
function preFetchInSerial(prefetchApp) {
|
|
3566
4495
|
return new Promise((resolve) => {
|
|
3567
4496
|
requestIdleCallback(() => {
|
|
3568
|
-
var _a, _b;
|
|
4497
|
+
var _a, _b, _c;
|
|
3569
4498
|
if (isPlainObject(prefetchApp) && navigator.onLine) {
|
|
3570
4499
|
prefetchApp.name = formatAppName(prefetchApp.name);
|
|
3571
4500
|
prefetchApp.url = formatAppURL(prefetchApp.url, prefetchApp.name);
|
|
@@ -3575,6 +4504,7 @@ function preFetchInSerial(prefetchApp) {
|
|
|
3575
4504
|
url: prefetchApp.url,
|
|
3576
4505
|
scopecss: !((_a = prefetchApp.disableScopecss) !== null && _a !== void 0 ? _a : microApp.disableScopecss),
|
|
3577
4506
|
useSandbox: !((_b = prefetchApp.disableSandbox) !== null && _b !== void 0 ? _b : microApp.disableSandbox),
|
|
4507
|
+
useMemoryRouter: !((_c = prefetchApp.disableMemoryRouter) !== null && _c !== void 0 ? _c : microApp.disableMemoryRouter),
|
|
3578
4508
|
});
|
|
3579
4509
|
app.isPrefetch = true;
|
|
3580
4510
|
app.prefetchResolve = resolve;
|
|
@@ -3643,7 +4573,7 @@ function getAllApps() {
|
|
|
3643
4573
|
/**
|
|
3644
4574
|
* unmount app by appName
|
|
3645
4575
|
* @param appName
|
|
3646
|
-
* @param options
|
|
4576
|
+
* @param options unmountAppOptions
|
|
3647
4577
|
* @returns Promise<void>
|
|
3648
4578
|
*/
|
|
3649
4579
|
function unmountApp(appName, options) {
|
|
@@ -3717,6 +4647,7 @@ class MicroApp extends EventCenterForBaseApp {
|
|
|
3717
4647
|
super(...arguments);
|
|
3718
4648
|
this.tagName = 'micro-app';
|
|
3719
4649
|
this.preFetch = preFetch;
|
|
4650
|
+
this.router = router;
|
|
3720
4651
|
}
|
|
3721
4652
|
start(options) {
|
|
3722
4653
|
if (!isBrowser || !window.customElements) {
|
|
@@ -3746,6 +4677,7 @@ class MicroApp extends EventCenterForBaseApp {
|
|
|
3746
4677
|
this.inline = options.inline;
|
|
3747
4678
|
this.disableScopecss = options.disableScopecss;
|
|
3748
4679
|
this.disableSandbox = options.disableSandbox;
|
|
4680
|
+
this.disableMemoryRouter = options.disableMemoryRouter;
|
|
3749
4681
|
this.ssr = options.ssr;
|
|
3750
4682
|
isFunction(options.fetch) && (this.fetch = options.fetch);
|
|
3751
4683
|
isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);
|