@micro-zoe/micro-app 0.8.5 → 1.0.0-alpha.1
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 +4 -4
- package/README.zh-cn.md +9 -15
- package/lib/index.d.ts +64 -20
- package/lib/index.esm.js +1487 -304
- 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 +2 -2
- package/typings/global.d.ts +158 -2
package/lib/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const version = '0.
|
|
1
|
+
const version = '1.0.0-alpha.1';
|
|
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,12 +92,22 @@ 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
|
|
86
108
|
*/
|
|
87
109
|
function addProtocol(url) {
|
|
88
|
-
return url.startsWith('//') ? `${location.protocol}${url}` : url;
|
|
110
|
+
return url.startsWith('//') ? `${globalThis.location.protocol}${url}` : url;
|
|
89
111
|
}
|
|
90
112
|
/**
|
|
91
113
|
* format 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,
|
|
@@ -176,24 +199,15 @@ function promiseStream(promiseList, successCb, errorCb, finallyCb) {
|
|
|
176
199
|
promiseList.forEach((p, i) => {
|
|
177
200
|
if (isPromise(p)) {
|
|
178
201
|
p.then((res) => {
|
|
179
|
-
successCb({
|
|
180
|
-
data: res,
|
|
181
|
-
index: i,
|
|
182
|
-
});
|
|
202
|
+
successCb({ data: res, index: i });
|
|
183
203
|
isFinished();
|
|
184
204
|
}).catch((err) => {
|
|
185
|
-
errorCb({
|
|
186
|
-
error: err,
|
|
187
|
-
index: i,
|
|
188
|
-
});
|
|
205
|
+
errorCb({ error: err, index: i });
|
|
189
206
|
isFinished();
|
|
190
207
|
});
|
|
191
208
|
}
|
|
192
209
|
else {
|
|
193
|
-
successCb({
|
|
194
|
-
data: p,
|
|
195
|
-
index: i,
|
|
196
|
-
});
|
|
210
|
+
successCb({ data: p, index: i });
|
|
197
211
|
isFinished();
|
|
198
212
|
}
|
|
199
213
|
});
|
|
@@ -306,6 +320,98 @@ function trim(str) {
|
|
|
306
320
|
function isFireFox() {
|
|
307
321
|
return navigator.userAgent.indexOf('Firefox') > -1;
|
|
308
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Transforms a queryString into object.
|
|
325
|
+
* @param search - search string to parse
|
|
326
|
+
* @returns a query object
|
|
327
|
+
*/
|
|
328
|
+
function parseQuery(search) {
|
|
329
|
+
const result = {};
|
|
330
|
+
const queryList = search.split('&');
|
|
331
|
+
// we will not decode the key/value to ensure that the values are consistent when update URL
|
|
332
|
+
for (const queryItem of queryList) {
|
|
333
|
+
const eqPos = queryItem.indexOf('=');
|
|
334
|
+
const key = eqPos < 0 ? queryItem : queryItem.slice(0, eqPos);
|
|
335
|
+
const value = eqPos < 0 ? null : queryItem.slice(eqPos + 1);
|
|
336
|
+
if (key in result) {
|
|
337
|
+
let currentValue = result[key];
|
|
338
|
+
if (!isArray(currentValue)) {
|
|
339
|
+
currentValue = result[key] = [currentValue];
|
|
340
|
+
}
|
|
341
|
+
currentValue.push(value);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
result[key] = value;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Transforms an object to query string
|
|
351
|
+
* @param queryObject - query object to stringify
|
|
352
|
+
* @returns query string without the leading `?`
|
|
353
|
+
*/
|
|
354
|
+
function stringifyQuery(queryObject) {
|
|
355
|
+
let result = '';
|
|
356
|
+
for (const key in queryObject) {
|
|
357
|
+
const value = queryObject[key];
|
|
358
|
+
if (isNull(value)) {
|
|
359
|
+
result += (result.length ? '&' : '') + key;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
const valueList = isArray(value) ? value : [value];
|
|
363
|
+
valueList.forEach(value => {
|
|
364
|
+
if (!isUndefined(value)) {
|
|
365
|
+
result += (result.length ? '&' : '') + key;
|
|
366
|
+
if (!isNull(value))
|
|
367
|
+
result += '=' + value;
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Register or unregister callback/guard with Set
|
|
376
|
+
*/
|
|
377
|
+
function useSetRecord() {
|
|
378
|
+
const handlers = new Set();
|
|
379
|
+
function add(handler) {
|
|
380
|
+
handlers.add(handler);
|
|
381
|
+
return () => {
|
|
382
|
+
if (handlers.has(handler))
|
|
383
|
+
return handlers.delete(handler);
|
|
384
|
+
return false;
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
add,
|
|
389
|
+
list: () => handlers,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* record data with Map
|
|
394
|
+
*/
|
|
395
|
+
function useMapRecord() {
|
|
396
|
+
const data = new Map();
|
|
397
|
+
function add(key, value) {
|
|
398
|
+
data.set(key, value);
|
|
399
|
+
return () => {
|
|
400
|
+
if (data.has(key))
|
|
401
|
+
return data.delete(key);
|
|
402
|
+
return false;
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
add,
|
|
407
|
+
get: (key) => data.get(key),
|
|
408
|
+
delete: (key) => {
|
|
409
|
+
if (data.has(key))
|
|
410
|
+
return data.delete(key);
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
309
415
|
|
|
310
416
|
var ObservedAttrName;
|
|
311
417
|
(function (ObservedAttrName) {
|
|
@@ -315,13 +421,13 @@ var ObservedAttrName;
|
|
|
315
421
|
// app status
|
|
316
422
|
var appStates;
|
|
317
423
|
(function (appStates) {
|
|
318
|
-
appStates["
|
|
319
|
-
appStates["
|
|
320
|
-
appStates["
|
|
321
|
-
appStates["
|
|
322
|
-
appStates["MOUNTING"] = "
|
|
323
|
-
appStates["MOUNTED"] = "
|
|
324
|
-
appStates["UNMOUNT"] = "
|
|
424
|
+
appStates["CREATED"] = "created";
|
|
425
|
+
appStates["LOADING"] = "loading";
|
|
426
|
+
appStates["LOADED"] = "loaded";
|
|
427
|
+
appStates["LOAD_FAILED"] = "load_failed";
|
|
428
|
+
appStates["MOUNTING"] = "mounting";
|
|
429
|
+
appStates["MOUNTED"] = "mounted";
|
|
430
|
+
appStates["UNMOUNT"] = "unmount";
|
|
325
431
|
})(appStates || (appStates = {}));
|
|
326
432
|
// lifecycles
|
|
327
433
|
var lifeCycles;
|
|
@@ -339,16 +445,24 @@ var lifeCycles;
|
|
|
339
445
|
// keep-alive status
|
|
340
446
|
var keepAliveStates;
|
|
341
447
|
(function (keepAliveStates) {
|
|
342
|
-
keepAliveStates["KEEP_ALIVE_SHOW"] = "
|
|
343
|
-
keepAliveStates["KEEP_ALIVE_HIDDEN"] = "
|
|
448
|
+
keepAliveStates["KEEP_ALIVE_SHOW"] = "keep_alive_show";
|
|
449
|
+
keepAliveStates["KEEP_ALIVE_HIDDEN"] = "keep_alive_hidden";
|
|
344
450
|
})(keepAliveStates || (keepAliveStates = {}));
|
|
345
|
-
|
|
451
|
+
/**
|
|
452
|
+
* global key must be static key, they can not rewrite
|
|
453
|
+
* e.g.
|
|
454
|
+
* window.Promise = newValue
|
|
455
|
+
* new Promise ==> still get old value, not newValue, because they are cached by top function
|
|
456
|
+
* NOTE:
|
|
457
|
+
* 1. Do not add fetch, XMLHttpRequest, EventSource
|
|
458
|
+
*/
|
|
459
|
+
const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,Math,Number,Symbol,Date,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';
|
|
346
460
|
|
|
347
461
|
/**
|
|
348
462
|
* fetch source of html, js, css
|
|
349
463
|
* @param url source path
|
|
350
464
|
* @param appName app name
|
|
351
|
-
* @param config
|
|
465
|
+
* @param config fetch options
|
|
352
466
|
*/
|
|
353
467
|
function fetchSource(url, appName = null, options = {}) {
|
|
354
468
|
if (isFunction(microApp.fetch)) {
|
|
@@ -900,6 +1014,64 @@ function formatDynamicLink(url, info, app, originLink, replaceStyle) {
|
|
|
900
1014
|
});
|
|
901
1015
|
}
|
|
902
1016
|
|
|
1017
|
+
class Adapter {
|
|
1018
|
+
constructor() {
|
|
1019
|
+
// keys that can only assigned to rawWindow
|
|
1020
|
+
this.escapeSetterKeyList = [
|
|
1021
|
+
'location',
|
|
1022
|
+
];
|
|
1023
|
+
// keys that can escape to rawWindow
|
|
1024
|
+
this.staticEscapeProperties = [
|
|
1025
|
+
'System',
|
|
1026
|
+
'__cjsWrapper',
|
|
1027
|
+
];
|
|
1028
|
+
// keys that scoped in child app
|
|
1029
|
+
this.staticScopeProperties = [
|
|
1030
|
+
'webpackJsonp',
|
|
1031
|
+
'webpackHotUpdate',
|
|
1032
|
+
'Vue',
|
|
1033
|
+
];
|
|
1034
|
+
this.injectReactHRMProperty();
|
|
1035
|
+
}
|
|
1036
|
+
// TODO: __DEV__ process.env.NODE_ENV !== 'production'
|
|
1037
|
+
// adapter for react
|
|
1038
|
+
injectReactHRMProperty() {
|
|
1039
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1040
|
+
// react child in non-react env
|
|
1041
|
+
this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
|
|
1042
|
+
// in react parent
|
|
1043
|
+
if (globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__) {
|
|
1044
|
+
this.staticScopeProperties = this.staticScopeProperties.concat([
|
|
1045
|
+
'__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
|
|
1046
|
+
'__reactRefreshInjected',
|
|
1047
|
+
]);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
// Fix conflict of babel-polyfill@6.x
|
|
1053
|
+
function fixBabelPolyfill6() {
|
|
1054
|
+
if (globalEnv.rawWindow._babelPolyfill)
|
|
1055
|
+
globalEnv.rawWindow._babelPolyfill = false;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Fix error of hot reload when parent&child created by create-react-app in development environment
|
|
1059
|
+
* Issue: https://github.com/micro-zoe/micro-app/issues/382
|
|
1060
|
+
*/
|
|
1061
|
+
function fixReactHMRConflict(app) {
|
|
1062
|
+
var _a;
|
|
1063
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1064
|
+
const rawReactErrorHook = globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
|
|
1065
|
+
const childReactErrorHook = (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
|
|
1066
|
+
if (rawReactErrorHook && childReactErrorHook) {
|
|
1067
|
+
globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = childReactErrorHook;
|
|
1068
|
+
defer(() => {
|
|
1069
|
+
globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = rawReactErrorHook;
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
903
1075
|
// Record element and map element
|
|
904
1076
|
const dynamicElementInMicroAppMap = new WeakMap();
|
|
905
1077
|
/**
|
|
@@ -974,51 +1146,55 @@ function handleNewNode(parent, child, app) {
|
|
|
974
1146
|
* @param passiveChild second param of insertBefore and replaceChild
|
|
975
1147
|
*/
|
|
976
1148
|
function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
|
|
1149
|
+
const hijackElement = getHijackElement(parent, app);
|
|
977
1150
|
/**
|
|
978
1151
|
* If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
|
|
979
1152
|
* E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
|
|
980
1153
|
*/
|
|
981
|
-
if (
|
|
982
|
-
const microAppHead = app.container.querySelector('micro-app-head');
|
|
1154
|
+
if (hijackElement) {
|
|
983
1155
|
/**
|
|
984
1156
|
* 1. If passiveChild exists, it must be insertBefore or replaceChild
|
|
985
1157
|
* 2. When removeChild, targetChild may not be in microAppHead or head
|
|
986
1158
|
*/
|
|
987
|
-
if (passiveChild && !
|
|
988
|
-
return globalEnv.rawAppendChild.call(
|
|
1159
|
+
if (passiveChild && !hijackElement.contains(passiveChild)) {
|
|
1160
|
+
return globalEnv.rawAppendChild.call(hijackElement, targetChild);
|
|
989
1161
|
}
|
|
990
|
-
else if (rawMethod === globalEnv.rawRemoveChild && !
|
|
1162
|
+
else if (rawMethod === globalEnv.rawRemoveChild && !hijackElement.contains(targetChild)) {
|
|
991
1163
|
if (parent.contains(targetChild)) {
|
|
992
1164
|
return rawMethod.call(parent, targetChild);
|
|
993
1165
|
}
|
|
994
1166
|
return targetChild;
|
|
995
1167
|
}
|
|
996
|
-
|
|
997
|
-
|
|
1168
|
+
// TODO: __DEV__
|
|
1169
|
+
if (process.env.NODE_ENV !== 'production' &&
|
|
1170
|
+
targetChild instanceof HTMLIFrameElement &&
|
|
1171
|
+
rawMethod === globalEnv.rawAppendChild) {
|
|
1172
|
+
fixReactHMRConflict(app);
|
|
998
1173
|
}
|
|
999
|
-
return rawMethod
|
|
1174
|
+
return invokeRawMethod(rawMethod, hijackElement, targetChild, passiveChild);
|
|
1000
1175
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
return rawMethod.call(parent, targetChild);
|
|
1009
|
-
}
|
|
1010
|
-
return targetChild;
|
|
1011
|
-
}
|
|
1012
|
-
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
1013
|
-
return rawMethod.call(microAppBody, targetChild);
|
|
1014
|
-
}
|
|
1015
|
-
return rawMethod.call(microAppBody, targetChild, passiveChild);
|
|
1176
|
+
return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
|
|
1177
|
+
}
|
|
1178
|
+
// head/body map to micro-app-head/micro-app-body
|
|
1179
|
+
function getHijackElement(node, app) {
|
|
1180
|
+
var _a, _b;
|
|
1181
|
+
if (node === document.head) {
|
|
1182
|
+
return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
|
|
1016
1183
|
}
|
|
1017
|
-
|
|
1184
|
+
if (node === document.body) {
|
|
1185
|
+
return (_b = app === null || app === void 0 ? void 0 : app.container) === null || _b === void 0 ? void 0 : _b.querySelector('micro-app-body');
|
|
1186
|
+
}
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
|
|
1190
|
+
if (isPendMethod(rawMethod)) {
|
|
1018
1191
|
return rawMethod.call(parent, targetChild);
|
|
1019
1192
|
}
|
|
1020
1193
|
return rawMethod.call(parent, targetChild, passiveChild);
|
|
1021
1194
|
}
|
|
1195
|
+
function isPendMethod(method) {
|
|
1196
|
+
return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
|
|
1197
|
+
}
|
|
1022
1198
|
// Get the map element
|
|
1023
1199
|
function getMappingNode(node) {
|
|
1024
1200
|
var _a;
|
|
@@ -1106,27 +1282,6 @@ function patchElementPrototypeMethods() {
|
|
|
1106
1282
|
this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
|
|
1107
1283
|
return clonedNode;
|
|
1108
1284
|
};
|
|
1109
|
-
// patch getBoundingClientRect
|
|
1110
|
-
// TODO: scenes test
|
|
1111
|
-
// Element.prototype.getBoundingClientRect = function getBoundingClientRect () {
|
|
1112
|
-
// const rawRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(this)
|
|
1113
|
-
// if (this.__MICRO_APP_NAME__) {
|
|
1114
|
-
// const app = appInstanceMap.get(this.__MICRO_APP_NAME__)
|
|
1115
|
-
// if (!app?.container) {
|
|
1116
|
-
// return rawRect
|
|
1117
|
-
// }
|
|
1118
|
-
// const appBody = app.container.querySelector('micro-app-body')
|
|
1119
|
-
// const appBodyRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(appBody)
|
|
1120
|
-
// const computedRect: DOMRect = new DOMRect(
|
|
1121
|
-
// rawRect.x - appBodyRect.x,
|
|
1122
|
-
// rawRect.y - appBodyRect.y,
|
|
1123
|
-
// rawRect.width,
|
|
1124
|
-
// rawRect.height,
|
|
1125
|
-
// )
|
|
1126
|
-
// return computedRect
|
|
1127
|
-
// }
|
|
1128
|
-
// return rawRect
|
|
1129
|
-
// }
|
|
1130
1285
|
}
|
|
1131
1286
|
/**
|
|
1132
1287
|
* Mark the newly created element in the micro application
|
|
@@ -1134,7 +1289,8 @@ function patchElementPrototypeMethods() {
|
|
|
1134
1289
|
*/
|
|
1135
1290
|
function markElement(element) {
|
|
1136
1291
|
const appName = getCurrentAppName();
|
|
1137
|
-
|
|
1292
|
+
if (appName)
|
|
1293
|
+
element.__MICRO_APP_NAME__ = appName;
|
|
1138
1294
|
return element;
|
|
1139
1295
|
}
|
|
1140
1296
|
// methods of document
|
|
@@ -1155,27 +1311,29 @@ function patchDocument() {
|
|
|
1155
1311
|
};
|
|
1156
1312
|
// query element👇
|
|
1157
1313
|
function querySelector(selectors) {
|
|
1158
|
-
var _a, _b, _c;
|
|
1314
|
+
var _a, _b, _c, _d;
|
|
1159
1315
|
const appName = getCurrentAppName();
|
|
1160
1316
|
if (!appName ||
|
|
1317
|
+
!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
|
|
1161
1318
|
!selectors ||
|
|
1162
1319
|
isUniqueElement(selectors) ||
|
|
1163
1320
|
// see https://github.com/micro-zoe/micro-app/issues/56
|
|
1164
1321
|
rawDocument !== this) {
|
|
1165
1322
|
return globalEnv.rawQuerySelector.call(this, selectors);
|
|
1166
1323
|
}
|
|
1167
|
-
return (
|
|
1324
|
+
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;
|
|
1168
1325
|
}
|
|
1169
1326
|
function querySelectorAll(selectors) {
|
|
1170
|
-
var _a, _b, _c;
|
|
1327
|
+
var _a, _b, _c, _d;
|
|
1171
1328
|
const appName = getCurrentAppName();
|
|
1172
1329
|
if (!appName ||
|
|
1330
|
+
!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
|
|
1173
1331
|
!selectors ||
|
|
1174
1332
|
isUniqueElement(selectors) ||
|
|
1175
1333
|
rawDocument !== this) {
|
|
1176
1334
|
return globalEnv.rawQuerySelectorAll.call(this, selectors);
|
|
1177
1335
|
}
|
|
1178
|
-
return (
|
|
1336
|
+
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 : [];
|
|
1179
1337
|
}
|
|
1180
1338
|
Document.prototype.querySelector = querySelector;
|
|
1181
1339
|
Document.prototype.querySelectorAll = querySelectorAll;
|
|
@@ -1243,10 +1401,9 @@ function patchSetAttribute() {
|
|
|
1243
1401
|
if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
|
|
1244
1402
|
if (isPlainObject(value)) {
|
|
1245
1403
|
const cloneValue = {};
|
|
1246
|
-
Object.getOwnPropertyNames(value).forEach((
|
|
1247
|
-
if (!(isString(
|
|
1248
|
-
|
|
1249
|
-
cloneValue[propertyKey] = value[propertyKey];
|
|
1404
|
+
Object.getOwnPropertyNames(value).forEach((key) => {
|
|
1405
|
+
if (!(isString(key) && key.indexOf('__') === 0)) {
|
|
1406
|
+
cloneValue[key] = value[key];
|
|
1250
1407
|
}
|
|
1251
1408
|
});
|
|
1252
1409
|
this.data = cloneValue;
|
|
@@ -1293,7 +1450,6 @@ function releasePatches() {
|
|
|
1293
1450
|
Element.prototype.append = globalEnv.rawAppend;
|
|
1294
1451
|
Element.prototype.prepend = globalEnv.rawPrepend;
|
|
1295
1452
|
Element.prototype.cloneNode = globalEnv.rawCloneNode;
|
|
1296
|
-
// Element.prototype.getBoundingClientRect = globalEnv.rawGetBoundingClientRect
|
|
1297
1453
|
}
|
|
1298
1454
|
// Set the style of micro-app-head and micro-app-body
|
|
1299
1455
|
let hasRejectMicroAppStyle = false;
|
|
@@ -1307,27 +1463,6 @@ function rejectMicroAppStyle() {
|
|
|
1307
1463
|
}
|
|
1308
1464
|
}
|
|
1309
1465
|
|
|
1310
|
-
function unmountNestedApp() {
|
|
1311
|
-
releaseUnmountOfNestedApp();
|
|
1312
|
-
appInstanceMap.forEach(app => {
|
|
1313
|
-
// @ts-ignore
|
|
1314
|
-
app.container && getRootContainer(app.container).disconnectedCallback();
|
|
1315
|
-
});
|
|
1316
|
-
!window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
|
|
1317
|
-
}
|
|
1318
|
-
// if micro-app run in micro application, delete all next generation application when unmount event received
|
|
1319
|
-
function listenUmountOfNestedApp() {
|
|
1320
|
-
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
1321
|
-
window.addEventListener('unmount', unmountNestedApp, false);
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
// release listener
|
|
1325
|
-
function releaseUnmountOfNestedApp() {
|
|
1326
|
-
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
1327
|
-
window.removeEventListener('unmount', unmountNestedApp, false);
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
1466
|
const globalEnv = {};
|
|
1332
1467
|
/**
|
|
1333
1468
|
* Note loop nesting
|
|
@@ -1347,7 +1482,6 @@ function initGlobalEnv() {
|
|
|
1347
1482
|
const rawAppend = Element.prototype.append;
|
|
1348
1483
|
const rawPrepend = Element.prototype.prepend;
|
|
1349
1484
|
const rawCloneNode = Element.prototype.cloneNode;
|
|
1350
|
-
// const rawGetBoundingClientRect = Element.prototype.getBoundingClientRect
|
|
1351
1485
|
const rawCreateElement = Document.prototype.createElement;
|
|
1352
1486
|
const rawCreateElementNS = Document.prototype.createElementNS;
|
|
1353
1487
|
const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
|
|
@@ -1381,7 +1515,7 @@ function initGlobalEnv() {
|
|
|
1381
1515
|
const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
|
|
1382
1516
|
// mark current application as base application
|
|
1383
1517
|
window.__MICRO_APP_BASE_APPLICATION__ = true;
|
|
1384
|
-
|
|
1518
|
+
assign(globalEnv, {
|
|
1385
1519
|
// source/patch
|
|
1386
1520
|
rawSetAttribute,
|
|
1387
1521
|
rawAppendChild,
|
|
@@ -1391,7 +1525,6 @@ function initGlobalEnv() {
|
|
|
1391
1525
|
rawAppend,
|
|
1392
1526
|
rawPrepend,
|
|
1393
1527
|
rawCloneNode,
|
|
1394
|
-
// rawGetBoundingClientRect,
|
|
1395
1528
|
rawCreateElement,
|
|
1396
1529
|
rawCreateElementNS,
|
|
1397
1530
|
rawCreateDocumentFragment,
|
|
@@ -1418,8 +1551,6 @@ function initGlobalEnv() {
|
|
|
1418
1551
|
});
|
|
1419
1552
|
// global effect
|
|
1420
1553
|
rejectMicroAppStyle();
|
|
1421
|
-
releaseUnmountOfNestedApp();
|
|
1422
|
-
listenUmountOfNestedApp();
|
|
1423
1554
|
}
|
|
1424
1555
|
}
|
|
1425
1556
|
|
|
@@ -2140,6 +2271,28 @@ function rebuildDataCenterSnapshot(microAppEventCenter) {
|
|
|
2140
2271
|
}
|
|
2141
2272
|
}
|
|
2142
2273
|
|
|
2274
|
+
function unmountNestedApp() {
|
|
2275
|
+
appInstanceMap.forEach(app => {
|
|
2276
|
+
// @ts-ignore
|
|
2277
|
+
app.container && getRootContainer(app.container).disconnectedCallback();
|
|
2278
|
+
});
|
|
2279
|
+
!window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
|
|
2280
|
+
}
|
|
2281
|
+
// release listener
|
|
2282
|
+
function releaseUnmountOfNestedApp() {
|
|
2283
|
+
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
2284
|
+
window.removeEventListener('unmount', unmountNestedApp, false);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
// if micro-app run in micro application, delete all next generation application when unmount event received
|
|
2288
|
+
// unmount event will auto release by sandbox
|
|
2289
|
+
function listenUmountOfNestedApp() {
|
|
2290
|
+
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
2291
|
+
releaseUnmountOfNestedApp();
|
|
2292
|
+
window.addEventListener('unmount', unmountNestedApp, false);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2143
2296
|
/* eslint-disable no-return-assign */
|
|
2144
2297
|
function isBoundedFunction(value) {
|
|
2145
2298
|
if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
|
|
@@ -2179,6 +2332,21 @@ function bindFunctionToRawWindow(rawWindow, value) {
|
|
|
2179
2332
|
return value;
|
|
2180
2333
|
}
|
|
2181
2334
|
|
|
2335
|
+
// this events should be sent to the specified app
|
|
2336
|
+
const formatEventList = ['unmount', 'appstate-change'];
|
|
2337
|
+
/**
|
|
2338
|
+
* Format event name
|
|
2339
|
+
* @param eventName event name
|
|
2340
|
+
* @param appName app name
|
|
2341
|
+
*/
|
|
2342
|
+
function formatEventName$1(eventName, appName) {
|
|
2343
|
+
var _a;
|
|
2344
|
+
if (formatEventList.includes(eventName) ||
|
|
2345
|
+
((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter))) {
|
|
2346
|
+
return `${eventName}-${appName}`;
|
|
2347
|
+
}
|
|
2348
|
+
return eventName;
|
|
2349
|
+
}
|
|
2182
2350
|
// document.onclick binding list, the binding function of each application is unique
|
|
2183
2351
|
const documentClickListMap = new Map();
|
|
2184
2352
|
let hasRewriteDocumentOnClick = false;
|
|
@@ -2272,32 +2440,18 @@ function releaseEffectDocumentEvent() {
|
|
|
2272
2440
|
document.addEventListener = globalEnv.rawDocumentAddEventListener;
|
|
2273
2441
|
document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
|
|
2274
2442
|
}
|
|
2275
|
-
// this events should be sent to the specified app
|
|
2276
|
-
const formatEventList = ['unmount', 'appstate-change'];
|
|
2277
|
-
/**
|
|
2278
|
-
* Format event name
|
|
2279
|
-
* @param type event name
|
|
2280
|
-
* @param microAppWindow micro window
|
|
2281
|
-
*/
|
|
2282
|
-
function formatEventType(type, microAppWindow) {
|
|
2283
|
-
if (formatEventList.includes(type)) {
|
|
2284
|
-
return `${type}-${microAppWindow.__MICRO_APP_NAME__}`;
|
|
2285
|
-
}
|
|
2286
|
-
return type;
|
|
2287
|
-
}
|
|
2288
2443
|
/**
|
|
2289
2444
|
* Rewrite side-effect events
|
|
2290
2445
|
* @param microAppWindow micro window
|
|
2291
2446
|
*/
|
|
2292
|
-
function effect(microAppWindow) {
|
|
2293
|
-
const appName = microAppWindow.__MICRO_APP_NAME__;
|
|
2447
|
+
function effect(appName, microAppWindow) {
|
|
2294
2448
|
const eventListenerMap = new Map();
|
|
2295
2449
|
const intervalIdMap = new Map();
|
|
2296
2450
|
const timeoutIdMap = new Map();
|
|
2297
2451
|
const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
|
|
2298
2452
|
// listener may be null, e.g test-passive
|
|
2299
2453
|
microAppWindow.addEventListener = function (type, listener, options) {
|
|
2300
|
-
type =
|
|
2454
|
+
type = formatEventName$1(type, appName);
|
|
2301
2455
|
const listenerList = eventListenerMap.get(type);
|
|
2302
2456
|
if (listenerList) {
|
|
2303
2457
|
listenerList.add(listener);
|
|
@@ -2309,7 +2463,7 @@ function effect(microAppWindow) {
|
|
|
2309
2463
|
rawWindowAddEventListener.call(rawWindow, type, listener, options);
|
|
2310
2464
|
};
|
|
2311
2465
|
microAppWindow.removeEventListener = function (type, listener, options) {
|
|
2312
|
-
type =
|
|
2466
|
+
type = formatEventName$1(type, appName);
|
|
2313
2467
|
const listenerList = eventListenerMap.get(type);
|
|
2314
2468
|
if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
|
|
2315
2469
|
listenerList.delete(listener);
|
|
@@ -2436,125 +2590,1015 @@ function effect(microAppWindow) {
|
|
|
2436
2590
|
};
|
|
2437
2591
|
}
|
|
2438
2592
|
|
|
2439
|
-
//
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
]
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
*/
|
|
2455
|
-
this.scopeProperties = ['webpackJsonp', 'Vue'];
|
|
2456
|
-
// Properties that can be escape to rawWindow
|
|
2457
|
-
this.escapeProperties = [];
|
|
2458
|
-
// Properties newly added to microAppWindow
|
|
2459
|
-
this.injectedKeys = new Set();
|
|
2460
|
-
// Properties escape to rawWindow, cleared when unmount
|
|
2461
|
-
this.escapeKeys = new Set();
|
|
2462
|
-
// sandbox state
|
|
2463
|
-
this.active = false;
|
|
2464
|
-
this.microAppWindow = {}; // Proxy target
|
|
2465
|
-
// get scopeProperties and escapeProperties from plugins
|
|
2466
|
-
this.getSpecialProperties(appName);
|
|
2467
|
-
// create proxyWindow with Proxy(microAppWindow)
|
|
2468
|
-
this.proxyWindow = this.createProxyWindow(appName);
|
|
2469
|
-
// inject global properties
|
|
2470
|
-
this.initMicroAppWindow(this.microAppWindow, appName, url);
|
|
2471
|
-
// Rewrite global event listener & timeout
|
|
2472
|
-
Object.assign(this, effect(this.microAppWindow));
|
|
2473
|
-
}
|
|
2474
|
-
start(baseRoute) {
|
|
2475
|
-
if (!this.active) {
|
|
2476
|
-
this.active = true;
|
|
2477
|
-
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
|
|
2478
|
-
// BUG FIX: bable-polyfill@6.x
|
|
2479
|
-
globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
|
|
2480
|
-
if (++SandBox.activeCount === 1) {
|
|
2481
|
-
effectDocumentEvent();
|
|
2482
|
-
patchElementPrototypeMethods();
|
|
2483
|
-
}
|
|
2593
|
+
// set micro app state to origin state
|
|
2594
|
+
function setMicroState(appName, rawState, microState) {
|
|
2595
|
+
const additionalState = {
|
|
2596
|
+
microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
|
|
2597
|
+
[appName]: microState
|
|
2598
|
+
})
|
|
2599
|
+
};
|
|
2600
|
+
// create new state object
|
|
2601
|
+
return assign({}, rawState, additionalState);
|
|
2602
|
+
}
|
|
2603
|
+
// delete micro app state form origin state
|
|
2604
|
+
function removeMicroState(appName, rawState) {
|
|
2605
|
+
if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
|
|
2606
|
+
if (!isUndefined(rawState.microAppState[appName])) {
|
|
2607
|
+
delete rawState.microAppState[appName];
|
|
2484
2608
|
}
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
if (this.active) {
|
|
2488
|
-
this.active = false;
|
|
2489
|
-
this.releaseEffect();
|
|
2490
|
-
this.microAppWindow.microApp.clearDataListener();
|
|
2491
|
-
this.microAppWindow.microApp.clearGlobalDataListener();
|
|
2492
|
-
this.injectedKeys.forEach((key) => {
|
|
2493
|
-
Reflect.deleteProperty(this.microAppWindow, key);
|
|
2494
|
-
});
|
|
2495
|
-
this.injectedKeys.clear();
|
|
2496
|
-
this.escapeKeys.forEach((key) => {
|
|
2497
|
-
Reflect.deleteProperty(globalEnv.rawWindow, key);
|
|
2498
|
-
});
|
|
2499
|
-
this.escapeKeys.clear();
|
|
2500
|
-
if (--SandBox.activeCount === 0) {
|
|
2501
|
-
releaseEffectDocumentEvent();
|
|
2502
|
-
releasePatches();
|
|
2503
|
-
}
|
|
2609
|
+
if (!Object.keys(rawState.microAppState).length) {
|
|
2610
|
+
delete rawState.microAppState;
|
|
2504
2611
|
}
|
|
2505
2612
|
}
|
|
2506
|
-
//
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2613
|
+
// 生成新的state对象
|
|
2614
|
+
return assign({}, rawState);
|
|
2615
|
+
}
|
|
2616
|
+
// get micro app state form origin state
|
|
2617
|
+
function getMicroState(appName, state) {
|
|
2618
|
+
var _a;
|
|
2619
|
+
return ((_a = state === null || state === void 0 ? void 0 : state.microAppState) === null || _a === void 0 ? void 0 : _a[appName]) || null;
|
|
2620
|
+
}
|
|
2621
|
+
const ENC_AD_RE = /&/g; // %M1
|
|
2622
|
+
const ENC_EQ_RE = /=/g; // %M2
|
|
2623
|
+
const DEC_AD_RE = /%M1/g; // &
|
|
2624
|
+
const DEC_EQ_RE = /%M2/g; // =
|
|
2625
|
+
function encodeMicroPath(path) {
|
|
2626
|
+
return encodeURIComponent(commonDecode(path).replace(ENC_AD_RE, '%M1').replace(ENC_EQ_RE, '%M2'));
|
|
2627
|
+
}
|
|
2628
|
+
function decodeMicroPath(path) {
|
|
2629
|
+
return commonDecode(path).replace(DEC_AD_RE, '&').replace(DEC_EQ_RE, '=');
|
|
2630
|
+
}
|
|
2631
|
+
function commonDecode(path) {
|
|
2632
|
+
try {
|
|
2633
|
+
const decPath = decodeURIComponent(path);
|
|
2634
|
+
if (path === decPath || DEC_AD_RE.test(decPath) || DEC_EQ_RE.test(decPath))
|
|
2635
|
+
return decPath;
|
|
2636
|
+
return commonDecode(decPath);
|
|
2515
2637
|
}
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
this.recordUmdInjectedValues.forEach((value, key) => {
|
|
2519
|
-
Reflect.set(this.proxyWindow, key, value);
|
|
2520
|
-
});
|
|
2521
|
-
this.rebuildUmdEffect();
|
|
2522
|
-
rebuildDataCenterSnapshot(this.microAppWindow.microApp);
|
|
2638
|
+
catch (_a) {
|
|
2639
|
+
return path;
|
|
2523
2640
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2641
|
+
}
|
|
2642
|
+
// 格式化query参数key,防止与原有参数的冲突
|
|
2643
|
+
function formatQueryAppName(appName) {
|
|
2644
|
+
return `app-${appName}`;
|
|
2645
|
+
}
|
|
2646
|
+
// 根据浏览器url参数,获取当前子应用的path
|
|
2647
|
+
function getMicroPathFromURL(appName) {
|
|
2648
|
+
var _a, _b;
|
|
2649
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
2650
|
+
const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
|
|
2651
|
+
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)]);
|
|
2652
|
+
return isString(microPath) ? decodeMicroPath(microPath) : null;
|
|
2653
|
+
}
|
|
2654
|
+
// 将name=encodeUrl地址插入到浏览器url上
|
|
2655
|
+
function setMicroPathToURL(appName, microLocation) {
|
|
2656
|
+
let { pathname, search, hash } = globalEnv.rawWindow.location;
|
|
2657
|
+
const queryObject = getQueryObjectFromURL(search, hash);
|
|
2658
|
+
const encodedMicroPath = encodeMicroPath(microLocation.pathname +
|
|
2659
|
+
microLocation.search +
|
|
2660
|
+
microLocation.hash);
|
|
2661
|
+
let isAttach2Hash = false; // 基座是否是hash模式,这个其实也不准,只是表示参数加到了hash上
|
|
2662
|
+
// hash存在且search不存在,则认为是hash路由
|
|
2663
|
+
if (hash && !search) {
|
|
2664
|
+
isAttach2Hash = true;
|
|
2665
|
+
if (queryObject.hashQuery) {
|
|
2666
|
+
queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
|
|
2667
|
+
}
|
|
2668
|
+
else {
|
|
2669
|
+
queryObject.hashQuery = {
|
|
2670
|
+
[formatQueryAppName(appName)]: encodedMicroPath
|
|
2671
|
+
};
|
|
2672
|
+
}
|
|
2673
|
+
const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?';
|
|
2674
|
+
hash = baseHash + stringifyQuery(queryObject.hashQuery);
|
|
2534
2675
|
}
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
if (isArray(plugin.escapeProperties)) {
|
|
2544
|
-
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2676
|
+
else {
|
|
2677
|
+
if (queryObject.searchQuery) {
|
|
2678
|
+
queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath;
|
|
2679
|
+
}
|
|
2680
|
+
else {
|
|
2681
|
+
queryObject.searchQuery = {
|
|
2682
|
+
[formatQueryAppName(appName)]: encodedMicroPath
|
|
2683
|
+
};
|
|
2548
2684
|
}
|
|
2685
|
+
search = '?' + stringifyQuery(queryObject.searchQuery);
|
|
2549
2686
|
}
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2687
|
+
return {
|
|
2688
|
+
fullPath: pathname + search + hash,
|
|
2689
|
+
isAttach2Hash,
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2692
|
+
// 将name=encodeUrl的参数从浏览器url上删除
|
|
2693
|
+
function removeMicroPathFromURL(appName, targetLocation) {
|
|
2694
|
+
var _a, _b, _c, _d;
|
|
2695
|
+
let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
|
|
2696
|
+
const queryObject = getQueryObjectFromURL(search, hash);
|
|
2697
|
+
let isAttach2Hash = false;
|
|
2698
|
+
if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
|
|
2699
|
+
isAttach2Hash = true;
|
|
2700
|
+
(_b = queryObject.hashQuery) === null || _b === void 0 ? true : delete _b[formatQueryAppName(appName)];
|
|
2701
|
+
const hashQueryStr = stringifyQuery(queryObject.hashQuery);
|
|
2702
|
+
hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr;
|
|
2703
|
+
}
|
|
2704
|
+
else if ((_c = queryObject.searchQuery) === null || _c === void 0 ? void 0 : _c[formatQueryAppName(appName)]) {
|
|
2705
|
+
(_d = queryObject.searchQuery) === null || _d === void 0 ? true : delete _d[formatQueryAppName(appName)];
|
|
2706
|
+
const searchQueryStr = stringifyQuery(queryObject.searchQuery);
|
|
2707
|
+
search = searchQueryStr ? '?' + searchQueryStr : '';
|
|
2708
|
+
}
|
|
2709
|
+
return {
|
|
2710
|
+
fullPath: pathname + search + hash,
|
|
2711
|
+
isAttach2Hash,
|
|
2712
|
+
};
|
|
2713
|
+
}
|
|
2714
|
+
/**
|
|
2715
|
+
* 根据location获取query对象
|
|
2716
|
+
*/
|
|
2717
|
+
function getQueryObjectFromURL(search, hash) {
|
|
2718
|
+
const queryObject = {};
|
|
2719
|
+
if (search !== '' && search !== '?') {
|
|
2720
|
+
queryObject.searchQuery = parseQuery(search.slice(1));
|
|
2721
|
+
}
|
|
2722
|
+
if (hash.includes('?')) {
|
|
2723
|
+
queryObject.hashQuery = parseQuery(hash.slice(hash.indexOf('?') + 1));
|
|
2724
|
+
}
|
|
2725
|
+
return queryObject;
|
|
2726
|
+
}
|
|
2727
|
+
/**
|
|
2728
|
+
* get microApp path from browser URL without hash
|
|
2729
|
+
*/
|
|
2730
|
+
function getNoHashMicroPathFromURL(appName, baseUrl) {
|
|
2731
|
+
const microPath = getMicroPathFromURL(appName);
|
|
2732
|
+
if (!microPath)
|
|
2733
|
+
return '';
|
|
2734
|
+
const formatLocation = createURL(microPath, baseUrl);
|
|
2735
|
+
return formatLocation.origin + formatLocation.pathname + formatLocation.search;
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
/**
|
|
2739
|
+
* dispatch PopStateEvent & HashChangeEvent to child app
|
|
2740
|
+
* each child app will listen for popstate event when sandbox start
|
|
2741
|
+
* and release it when sandbox stop
|
|
2742
|
+
* @param appName app name
|
|
2743
|
+
* @returns release callback
|
|
2744
|
+
*/
|
|
2745
|
+
function addHistoryListener(appName) {
|
|
2746
|
+
const rawWindow = globalEnv.rawWindow;
|
|
2747
|
+
// handle popstate event and distribute to child app
|
|
2748
|
+
const popStateHandler = (e) => {
|
|
2749
|
+
// exclude hidden keep-alive app
|
|
2750
|
+
if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
|
|
2751
|
+
const microPath = getMicroPathFromURL(appName);
|
|
2752
|
+
const app = appInstanceMap.get(appName);
|
|
2753
|
+
const proxyWindow = app.sandBox.proxyWindow;
|
|
2754
|
+
let isHashChange = false;
|
|
2755
|
+
// for hashChangeEvent
|
|
2756
|
+
const oldHref = proxyWindow.location.href;
|
|
2757
|
+
// Do not attach micro state to url when microPath is empty
|
|
2758
|
+
if (microPath) {
|
|
2759
|
+
const oldHash = proxyWindow.location.hash;
|
|
2760
|
+
updateMicroLocation(appName, microPath, proxyWindow.location);
|
|
2761
|
+
isHashChange = proxyWindow.location.hash !== oldHash;
|
|
2762
|
+
}
|
|
2763
|
+
// dispatch formatted popStateEvent to child
|
|
2764
|
+
dispatchPopStateEventToMicroApp(appName, proxyWindow, rawWindow.history.state);
|
|
2765
|
+
// dispatch formatted hashChangeEvent to child when hash change
|
|
2766
|
+
if (isHashChange)
|
|
2767
|
+
dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref);
|
|
2768
|
+
// clear element scope before trigger event of next app
|
|
2769
|
+
removeDomScope();
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
rawWindow.addEventListener('popstate', popStateHandler);
|
|
2773
|
+
return () => {
|
|
2774
|
+
rawWindow.removeEventListener('popstate', popStateHandler);
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
/**
|
|
2778
|
+
* dispatch formatted popstate event to microApp
|
|
2779
|
+
* @param appName app name
|
|
2780
|
+
* @param proxyWindow sandbox window
|
|
2781
|
+
* @param eventState history.state
|
|
2782
|
+
*/
|
|
2783
|
+
function dispatchPopStateEventToMicroApp(appName, proxyWindow, eventState) {
|
|
2784
|
+
// create PopStateEvent named popstate-appName with sub app state
|
|
2785
|
+
const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName, eventState) });
|
|
2786
|
+
globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
|
|
2787
|
+
// call function window.onpopstate if it exists
|
|
2788
|
+
typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
|
|
2789
|
+
}
|
|
2790
|
+
/**
|
|
2791
|
+
* dispatch formatted hashchange event to microApp
|
|
2792
|
+
* @param appName app name
|
|
2793
|
+
* @param proxyWindow sandbox window
|
|
2794
|
+
* @param oldHref old href
|
|
2795
|
+
*/
|
|
2796
|
+
function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
|
|
2797
|
+
const newHashChangeEvent = new HashChangeEvent(formatEventName$1('hashchange', appName), {
|
|
2798
|
+
newURL: proxyWindow.location.href,
|
|
2799
|
+
oldURL: oldHref,
|
|
2800
|
+
});
|
|
2801
|
+
globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
|
|
2802
|
+
// call function window.onhashchange if it exists
|
|
2803
|
+
typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
|
|
2804
|
+
}
|
|
2805
|
+
/**
|
|
2806
|
+
* dispatch native PopStateEvent, simulate location behavior
|
|
2807
|
+
* @param onlyForBrowser only dispatch PopStateEvent to browser
|
|
2808
|
+
*/
|
|
2809
|
+
function dispatchNativePopStateEvent(onlyForBrowser) {
|
|
2810
|
+
const event = new PopStateEvent('popstate', { state: null });
|
|
2811
|
+
if (onlyForBrowser)
|
|
2812
|
+
event.onlyForBrowser = true;
|
|
2813
|
+
globalEnv.rawWindow.dispatchEvent(event);
|
|
2814
|
+
}
|
|
2815
|
+
/**
|
|
2816
|
+
* dispatch hashchange event to browser
|
|
2817
|
+
* @param oldHref old href of rawWindow.location
|
|
2818
|
+
*/
|
|
2819
|
+
function dispatchNativeHashChangeEvent(oldHref) {
|
|
2820
|
+
const newHashChangeEvent = new HashChangeEvent('hashchange', {
|
|
2821
|
+
newURL: globalEnv.rawWindow.location.href,
|
|
2822
|
+
oldURL: oldHref,
|
|
2823
|
+
});
|
|
2824
|
+
globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
|
|
2825
|
+
}
|
|
2826
|
+
/**
|
|
2827
|
+
* dispatch popstate & hashchange event to browser
|
|
2828
|
+
* @param onlyForBrowser only dispatch event to browser
|
|
2829
|
+
* @param oldHref old href of rawWindow.location
|
|
2830
|
+
*/
|
|
2831
|
+
function dispatchNativeEvent(onlyForBrowser, oldHref) {
|
|
2832
|
+
// clear element scope before dispatch global event
|
|
2833
|
+
removeDomScope();
|
|
2834
|
+
dispatchNativePopStateEvent(onlyForBrowser);
|
|
2835
|
+
if (oldHref) {
|
|
2836
|
+
dispatchNativeHashChangeEvent(oldHref);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
/**
|
|
2841
|
+
* create proxyHistory for microApp
|
|
2842
|
+
* MDN https://developer.mozilla.org/en-US/docs/Web/API/History
|
|
2843
|
+
* @param appName app name
|
|
2844
|
+
* @param microLocation microApp location
|
|
2845
|
+
*/
|
|
2846
|
+
function createMicroHistory(appName, microLocation) {
|
|
2847
|
+
const rawHistory = globalEnv.rawWindow.history;
|
|
2848
|
+
function getMicroHistoryMethod(methodName) {
|
|
2849
|
+
return function (...rests) {
|
|
2850
|
+
if ((methodName === 'pushState' || methodName === 'replaceState') &&
|
|
2851
|
+
(isString(rests[2]) || isURL(rests[2]))) {
|
|
2852
|
+
const targetLocation = createURL(rests[2], microLocation.href);
|
|
2853
|
+
if (targetLocation.origin === microLocation.origin) {
|
|
2854
|
+
navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rawHistory.state, rests[0]), rests[1]);
|
|
2855
|
+
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
|
|
2856
|
+
if (targetFullPath !== microLocation.fullPath) {
|
|
2857
|
+
updateMicroLocation(appName, targetFullPath, microLocation);
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
else {
|
|
2861
|
+
rawHistory[methodName].apply(rawHistory, rests);
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
else {
|
|
2865
|
+
rawHistory[methodName].apply(rawHistory, rests);
|
|
2866
|
+
}
|
|
2867
|
+
};
|
|
2868
|
+
}
|
|
2869
|
+
return new Proxy(rawHistory, {
|
|
2870
|
+
get(target, key) {
|
|
2871
|
+
if (key === 'state') {
|
|
2872
|
+
return getMicroState(appName, rawHistory.state);
|
|
2873
|
+
}
|
|
2874
|
+
else if (isFunction(Reflect.get(target, key))) {
|
|
2875
|
+
return getMicroHistoryMethod(key);
|
|
2876
|
+
}
|
|
2877
|
+
return Reflect.get(target, key);
|
|
2878
|
+
},
|
|
2879
|
+
set(target, key, value) {
|
|
2880
|
+
return Reflect.set(target, key, value);
|
|
2881
|
+
}
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2884
|
+
/**
|
|
2885
|
+
* navigate to new path base on native method of history
|
|
2886
|
+
* @param methodName pushState/replaceState
|
|
2887
|
+
* @param fullPath full path
|
|
2888
|
+
* @param state history.state, default is null
|
|
2889
|
+
* @param title history.title, default is ''
|
|
2890
|
+
*/
|
|
2891
|
+
function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
|
|
2892
|
+
globalEnv.rawWindow.history[methodName](state, title, fullPath);
|
|
2893
|
+
}
|
|
2894
|
+
/**
|
|
2895
|
+
* Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
|
|
2896
|
+
* Use scenes:
|
|
2897
|
+
* 1. mount/unmount through updateBrowserURL with limited popstateEvent
|
|
2898
|
+
* 2. proxyHistory.pushState/replaceState with limited popstateEvent
|
|
2899
|
+
* 3. api microApp.router.push/replace
|
|
2900
|
+
* 4. proxyLocation.hash = xxx
|
|
2901
|
+
* @param methodName pushState/replaceState
|
|
2902
|
+
* @param result result of add/remove microApp path on browser url
|
|
2903
|
+
* @param onlyForBrowser only dispatch event to browser
|
|
2904
|
+
* @param state history.state, not required
|
|
2905
|
+
* @param title history.title, not required
|
|
2906
|
+
*/
|
|
2907
|
+
function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, title) {
|
|
2908
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
2909
|
+
const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
|
|
2910
|
+
const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
|
|
2911
|
+
// navigate with native history method
|
|
2912
|
+
nativeHistoryNavigate(methodName, result.fullPath, state, title);
|
|
2913
|
+
if (oldFullPath !== result.fullPath)
|
|
2914
|
+
dispatchNativeEvent(onlyForBrowser, oldHref);
|
|
2915
|
+
}
|
|
2916
|
+
/**
|
|
2917
|
+
* update browser url when mount/unmount/hidden/show
|
|
2918
|
+
* @param result result of add/remove microApp path on browser url
|
|
2919
|
+
* @param state history.state
|
|
2920
|
+
*/
|
|
2921
|
+
function updateBrowserURL(result, state) {
|
|
2922
|
+
navigateWithNativeEvent('replaceState', result, true, state);
|
|
2923
|
+
}
|
|
2924
|
+
/**
|
|
2925
|
+
* When path is same, keep the microAppState in history.state
|
|
2926
|
+
* Fix bug of missing microAppState in next.js & angular
|
|
2927
|
+
* @param method history.pushState/replaceState
|
|
2928
|
+
*/
|
|
2929
|
+
function patchHistoryState(method) {
|
|
2930
|
+
const rawWindow = globalEnv.rawWindow;
|
|
2931
|
+
return function (...rests) {
|
|
2932
|
+
var _a;
|
|
2933
|
+
if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.microAppState) &&
|
|
2934
|
+
(!isPlainObject(rests[0]) || !rests[0].microAppState) &&
|
|
2935
|
+
(isString(rests[2]) || isURL(rests[2]))) {
|
|
2936
|
+
const currentHref = rawWindow.location.href;
|
|
2937
|
+
const targetLocation = createURL(rests[2], currentHref);
|
|
2938
|
+
if (targetLocation.href === currentHref) {
|
|
2939
|
+
rests[0] = assign({}, rests[0], {
|
|
2940
|
+
microAppState: rawWindow.history.state.microAppState,
|
|
2941
|
+
});
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
method.apply(rawWindow.history, rests);
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
let isReWriteHistoryState = false;
|
|
2948
|
+
/**
|
|
2949
|
+
* rewrite history.pushState/replaceState
|
|
2950
|
+
* used to fix the problem that the microAppState maybe missing when mainApp navigate to same path
|
|
2951
|
+
* e.g: when nextjs, angular receive popstate event, they will use history.replaceState to update browser url with a new state object
|
|
2952
|
+
*/
|
|
2953
|
+
function rewriteHistoryState() {
|
|
2954
|
+
// filter nest app
|
|
2955
|
+
if (!isReWriteHistoryState && !window.__MICRO_APP_ENVIRONMENT__) {
|
|
2956
|
+
isReWriteHistoryState = true;
|
|
2957
|
+
const rawWindow = globalEnv.rawWindow;
|
|
2958
|
+
rawWindow.history.pushState = patchHistoryState(rawWindow.history.pushState);
|
|
2959
|
+
rawWindow.history.replaceState = patchHistoryState(rawWindow.history.replaceState);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
function createRouterApi() {
|
|
2964
|
+
/**
|
|
2965
|
+
* common handler for router.push/router.replace method
|
|
2966
|
+
* @param appName app name
|
|
2967
|
+
* @param methodName replaceState/pushState
|
|
2968
|
+
* @param targetLocation target location
|
|
2969
|
+
* @param state to.state
|
|
2970
|
+
*/
|
|
2971
|
+
function navigateWithRawHistory(appName, methodName, targetLocation, state) {
|
|
2972
|
+
navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, globalEnv.rawWindow.history.state, state !== null && state !== void 0 ? state : null));
|
|
2973
|
+
// clear element scope after navigate
|
|
2974
|
+
removeDomScope();
|
|
2975
|
+
}
|
|
2976
|
+
/**
|
|
2977
|
+
* create method of router.push/replace
|
|
2978
|
+
* NOTE:
|
|
2979
|
+
* 1. The same fullPath will be blocked
|
|
2980
|
+
* 2. name & path is required
|
|
2981
|
+
* 3. path is fullPath except for the domain (the domain can be taken, but not valid)
|
|
2982
|
+
* @param replace use router.replace?
|
|
2983
|
+
*/
|
|
2984
|
+
function createNavigationMethod(replace) {
|
|
2985
|
+
return function (to) {
|
|
2986
|
+
const appName = formatAppName(to.name);
|
|
2987
|
+
// console.log(3333333, appInstanceMap.get(appName))
|
|
2988
|
+
if (appName && isString(to.path)) {
|
|
2989
|
+
const app = appInstanceMap.get(appName);
|
|
2990
|
+
if (app && !app.sandBox)
|
|
2991
|
+
return logError(`navigation failed, sandBox of app ${appName} is closed`);
|
|
2992
|
+
// active apps, include hidden keep-alive app
|
|
2993
|
+
if (getActiveApps().includes(appName)) {
|
|
2994
|
+
const microLocation = app.sandBox.proxyWindow.location;
|
|
2995
|
+
const targetLocation = createURL(to.path, microLocation.href);
|
|
2996
|
+
// Only get path data, even if the origin is different from microApp
|
|
2997
|
+
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
|
|
2998
|
+
if (microLocation.fullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
|
|
2999
|
+
const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
|
|
3000
|
+
navigateWithRawHistory(appName, methodName, targetLocation, to.state);
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
else {
|
|
3004
|
+
/**
|
|
3005
|
+
* app not exit or unmounted, update browser URL with replaceState
|
|
3006
|
+
* use base app location.origin as baseURL
|
|
3007
|
+
*/
|
|
3008
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
3009
|
+
const targetLocation = createURL(to.path, rawLocation.origin);
|
|
3010
|
+
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
|
|
3011
|
+
if (getMicroPathFromURL(appName) !== targetFullPath) {
|
|
3012
|
+
navigateWithRawHistory(appName, to.replace === false ? 'pushState' : 'replaceState', targetLocation, to.state);
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
else {
|
|
3017
|
+
logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`);
|
|
3018
|
+
}
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
// create method of router.go/back/forward
|
|
3022
|
+
function createRawHistoryMethod(methodName) {
|
|
3023
|
+
return function (...rests) {
|
|
3024
|
+
return globalEnv.rawWindow.history[methodName](...rests);
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
const beforeGuards = useSetRecord();
|
|
3028
|
+
const afterGuards = useSetRecord();
|
|
3029
|
+
/**
|
|
3030
|
+
* run all of beforeEach/afterEach guards
|
|
3031
|
+
* NOTE:
|
|
3032
|
+
* 1. Modify browser url first, and then run guards,
|
|
3033
|
+
* consistent with the browser forward & back button
|
|
3034
|
+
* 2. Note the element binding
|
|
3035
|
+
* @param appName app name
|
|
3036
|
+
* @param to target location
|
|
3037
|
+
* @param from old location
|
|
3038
|
+
* @param guards guards list
|
|
3039
|
+
*/
|
|
3040
|
+
function runGuards(appName, to, from, guards) {
|
|
3041
|
+
// clear element scope before execute function of parent
|
|
3042
|
+
removeDomScope();
|
|
3043
|
+
for (const guard of guards) {
|
|
3044
|
+
if (isFunction(guard)) {
|
|
3045
|
+
guard(appName, to, from);
|
|
3046
|
+
}
|
|
3047
|
+
else if (isPlainObject(guard) && isFunction(guard[appName])) {
|
|
3048
|
+
guard[appName](to, from);
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
/**
|
|
3053
|
+
* global hook for router
|
|
3054
|
+
* update router information base on microLocation
|
|
3055
|
+
* @param appName app name
|
|
3056
|
+
* @param microLocation location of microApp
|
|
3057
|
+
*/
|
|
3058
|
+
function executeNavigationGuard(appName, to, from) {
|
|
3059
|
+
router.current.set(appName, to);
|
|
3060
|
+
runGuards(appName, to, from, beforeGuards.list());
|
|
3061
|
+
requestIdleCallback(() => {
|
|
3062
|
+
runGuards(appName, to, from, afterGuards.list());
|
|
3063
|
+
});
|
|
3064
|
+
}
|
|
3065
|
+
function clearRouterWhenUnmount(appName) {
|
|
3066
|
+
router.current.delete(appName);
|
|
3067
|
+
}
|
|
3068
|
+
// defaultPage data
|
|
3069
|
+
const defaultPageRecord = useMapRecord();
|
|
3070
|
+
/**
|
|
3071
|
+
* defaultPage只在子应用初始化时生效,且优先级比浏览器上的子应用路由地址低
|
|
3072
|
+
* @param appName app name
|
|
3073
|
+
* @param path page path
|
|
3074
|
+
*/
|
|
3075
|
+
function setDefaultPage(appName, path) {
|
|
3076
|
+
appName = formatAppName(appName);
|
|
3077
|
+
if (!appName)
|
|
3078
|
+
return noopFalse;
|
|
3079
|
+
return defaultPageRecord.add(appName, path);
|
|
3080
|
+
}
|
|
3081
|
+
function removeDefaultPage(appName) {
|
|
3082
|
+
appName = formatAppName(appName);
|
|
3083
|
+
if (!appName)
|
|
3084
|
+
return false;
|
|
3085
|
+
return defaultPageRecord.delete(appName);
|
|
3086
|
+
}
|
|
3087
|
+
// Router API for developer
|
|
3088
|
+
const router = {
|
|
3089
|
+
current: new Map(),
|
|
3090
|
+
encode: encodeMicroPath,
|
|
3091
|
+
decode: decodeMicroPath,
|
|
3092
|
+
push: createNavigationMethod(false),
|
|
3093
|
+
replace: createNavigationMethod(true),
|
|
3094
|
+
go: createRawHistoryMethod('go'),
|
|
3095
|
+
back: createRawHistoryMethod('back'),
|
|
3096
|
+
forward: createRawHistoryMethod('forward'),
|
|
3097
|
+
beforeEach: beforeGuards.add,
|
|
3098
|
+
afterEach: afterGuards.add,
|
|
3099
|
+
// attachToURL: 将指定的子应用路由信息添加到浏览器地址上
|
|
3100
|
+
// attachAllToURL: 将所有正在运行的子应用路由信息添加到浏览器地址上
|
|
3101
|
+
setDefaultPage,
|
|
3102
|
+
removeDefaultPage,
|
|
3103
|
+
getDefaultPage: defaultPageRecord.get,
|
|
3104
|
+
};
|
|
3105
|
+
return {
|
|
3106
|
+
router,
|
|
3107
|
+
executeNavigationGuard,
|
|
3108
|
+
clearRouterWhenUnmount,
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
|
|
3112
|
+
|
|
3113
|
+
const shadowLocationKeys = ['href', 'pathname', 'search', 'hash'];
|
|
3114
|
+
// origin is readonly, so we ignore when updateMicroLocation
|
|
3115
|
+
const locationKeys = [...shadowLocationKeys, 'host', 'hostname', 'port', 'protocol', 'search'];
|
|
3116
|
+
// origin, fullPath is necessary for guardLocation
|
|
3117
|
+
const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
|
|
3118
|
+
/**
|
|
3119
|
+
* create guardLocation by microLocation, used for router guard
|
|
3120
|
+
*/
|
|
3121
|
+
function createGuardLocation(appName, microLocation) {
|
|
3122
|
+
const guardLocation = assign({ name: appName }, microLocation);
|
|
3123
|
+
// The prototype values on the URL needs to be manually transferred
|
|
3124
|
+
for (const key of guardLocationKeys)
|
|
3125
|
+
guardLocation[key] = microLocation[key];
|
|
3126
|
+
return guardLocation;
|
|
3127
|
+
}
|
|
3128
|
+
// for updateBrowserURLWithLocation when initial
|
|
3129
|
+
function autoTriggerNavigationGuard(appName, microLocation) {
|
|
3130
|
+
executeNavigationGuard(appName, createGuardLocation(appName, microLocation), createGuardLocation(appName, microLocation));
|
|
3131
|
+
}
|
|
3132
|
+
/**
|
|
3133
|
+
* The following scenes will trigger location update:
|
|
3134
|
+
* 1. pushState/replaceState
|
|
3135
|
+
* 2. popStateEvent
|
|
3136
|
+
* 3. query on browser url when init sub app
|
|
3137
|
+
* 4. set defaultPage when when init sub app
|
|
3138
|
+
* NOTE:
|
|
3139
|
+
* 1. update browser URL first, and then update microLocation
|
|
3140
|
+
* 2. the same fullPath will not trigger router guards
|
|
3141
|
+
* @param appName app name
|
|
3142
|
+
* @param path target path
|
|
3143
|
+
* @param base base url
|
|
3144
|
+
* @param microLocation micro app location
|
|
3145
|
+
* @param type auto prevent
|
|
3146
|
+
*/
|
|
3147
|
+
function updateMicroLocation(appName, path, microLocation, type) {
|
|
3148
|
+
const newLocation = createURL(path, microLocation.href);
|
|
3149
|
+
// record old values of microLocation to `from`
|
|
3150
|
+
const from = createGuardLocation(appName, microLocation);
|
|
3151
|
+
for (const key of locationKeys) {
|
|
3152
|
+
if (shadowLocationKeys.includes(key)) {
|
|
3153
|
+
// reference of shadowLocation
|
|
3154
|
+
microLocation.shadowLocation[key] = newLocation[key];
|
|
3155
|
+
}
|
|
3156
|
+
else {
|
|
3157
|
+
// @ts-ignore reference of microLocation
|
|
3158
|
+
microLocation[key] = newLocation[key];
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
// update latest values of microLocation to `to`
|
|
3162
|
+
const to = createGuardLocation(appName, microLocation);
|
|
3163
|
+
// The hook called only when fullPath changed
|
|
3164
|
+
if (type === 'auto' || (from.fullPath !== to.fullPath && type !== 'prevent')) {
|
|
3165
|
+
executeNavigationGuard(appName, to, from);
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
/**
|
|
3169
|
+
* Create location for microApp, each microApp has only one location object, it is a reference type
|
|
3170
|
+
* MDN https://developer.mozilla.org/en-US/docs/Web/API/Location
|
|
3171
|
+
* @param appName app name
|
|
3172
|
+
* @param url app url
|
|
3173
|
+
*/
|
|
3174
|
+
function createMicroLocation(appName, url) {
|
|
3175
|
+
const rawWindow = globalEnv.rawWindow;
|
|
3176
|
+
const rawLocation = rawWindow.location;
|
|
3177
|
+
// microLocation is the location of child app, it is globally unique
|
|
3178
|
+
const microLocation = createURL(url);
|
|
3179
|
+
// shadowLocation is the current location information (href, pathname, search, hash)
|
|
3180
|
+
const shadowLocation = {
|
|
3181
|
+
href: microLocation.href,
|
|
3182
|
+
pathname: microLocation.pathname,
|
|
3183
|
+
search: microLocation.search,
|
|
3184
|
+
hash: microLocation.hash,
|
|
3185
|
+
};
|
|
3186
|
+
/**
|
|
3187
|
+
* Common handler for href, assign, replace
|
|
3188
|
+
* It is mainly used to deal with special scenes about hash
|
|
3189
|
+
* @param value target path
|
|
3190
|
+
* @param methodName pushState/replaceState
|
|
3191
|
+
* @returns origin value or formatted value
|
|
3192
|
+
*/
|
|
3193
|
+
const commonHandler = (value, methodName) => {
|
|
3194
|
+
const targetLocation = createURL(value, microLocation.href);
|
|
3195
|
+
// Even if the origin is the same, developers still have the possibility of want to jump to a new page
|
|
3196
|
+
if (targetLocation.origin === microLocation.origin) {
|
|
3197
|
+
const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
|
|
3198
|
+
/**
|
|
3199
|
+
* change hash with location.href will not trigger the browser reload
|
|
3200
|
+
* so we use pushState & reload to imitate href behavior
|
|
3201
|
+
* NOTE:
|
|
3202
|
+
* 1. if child app only change hash, it should not trigger browser reload
|
|
3203
|
+
* 2. if address is same and has hash, it should not add route stack
|
|
3204
|
+
*/
|
|
3205
|
+
if (targetLocation.pathname === shadowLocation.pathname &&
|
|
3206
|
+
targetLocation.search === shadowLocation.search) {
|
|
3207
|
+
let oldHref = null;
|
|
3208
|
+
if (targetLocation.hash !== shadowLocation.hash) {
|
|
3209
|
+
if (setMicroPathResult.isAttach2Hash)
|
|
3210
|
+
oldHref = rawLocation.href;
|
|
3211
|
+
nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
|
|
3212
|
+
}
|
|
3213
|
+
if (targetLocation.hash) {
|
|
3214
|
+
dispatchNativeEvent(false, oldHref);
|
|
3215
|
+
}
|
|
3216
|
+
else {
|
|
3217
|
+
rawLocation.reload();
|
|
3218
|
+
}
|
|
3219
|
+
return void 0;
|
|
3220
|
+
/**
|
|
3221
|
+
* when baseApp is hash router, address change of child can not reload browser
|
|
3222
|
+
* so we imitate behavior of browser (reload)
|
|
3223
|
+
*/
|
|
3224
|
+
}
|
|
3225
|
+
else if (setMicroPathResult.isAttach2Hash) {
|
|
3226
|
+
nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
|
|
3227
|
+
rawLocation.reload();
|
|
3228
|
+
return void 0;
|
|
3229
|
+
}
|
|
3230
|
+
value = setMicroPathResult.fullPath;
|
|
3231
|
+
}
|
|
3232
|
+
return value;
|
|
3233
|
+
};
|
|
3234
|
+
/**
|
|
3235
|
+
* create location PropertyDescriptor (href, pathname, search, hash)
|
|
3236
|
+
* @param key property name
|
|
3237
|
+
* @param setter setter of location property
|
|
3238
|
+
*/
|
|
3239
|
+
function createPropertyDescriptor(getter, setter) {
|
|
3240
|
+
return {
|
|
3241
|
+
enumerable: true,
|
|
3242
|
+
configurable: true,
|
|
3243
|
+
get: getter,
|
|
3244
|
+
set: setter,
|
|
3245
|
+
};
|
|
3246
|
+
}
|
|
3247
|
+
/**
|
|
3248
|
+
* common handler for location.pathname & location.search
|
|
3249
|
+
* @param targetPath target fullPath
|
|
3250
|
+
* @param key pathname/search
|
|
3251
|
+
*/
|
|
3252
|
+
function handleForPathNameAndSearch(targetPath, key) {
|
|
3253
|
+
const targetLocation = createURL(targetPath, url);
|
|
3254
|
+
// When the browser url has a hash value, the same pathname/search will not refresh browser
|
|
3255
|
+
if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
|
|
3256
|
+
// The href has not changed, not need to dispatch hashchange event
|
|
3257
|
+
dispatchNativeEvent(false);
|
|
3258
|
+
}
|
|
3259
|
+
else {
|
|
3260
|
+
/**
|
|
3261
|
+
* When the value is the same, no new route stack will be added
|
|
3262
|
+
* Special scenes such as:
|
|
3263
|
+
* pathname: /path ==> /path#hash, /path ==> /path?query
|
|
3264
|
+
* search: ?query ==> ?query#hash
|
|
3265
|
+
*/
|
|
3266
|
+
nativeHistoryNavigate(targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
|
|
3267
|
+
rawLocation.reload();
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
/**
|
|
3271
|
+
* Special processing for four keys: href, pathname, search and hash
|
|
3272
|
+
* They take values from shadowLocation, and require special operations when assigning values
|
|
3273
|
+
*/
|
|
3274
|
+
rawDefineProperties(microLocation, {
|
|
3275
|
+
href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
|
|
3276
|
+
const targetPath = commonHandler(value, 'pushState');
|
|
3277
|
+
if (targetPath)
|
|
3278
|
+
rawLocation.href = targetPath;
|
|
3279
|
+
}),
|
|
3280
|
+
pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
|
|
3281
|
+
const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
|
|
3282
|
+
handleForPathNameAndSearch(targetPath, 'pathname');
|
|
3283
|
+
}),
|
|
3284
|
+
search: createPropertyDescriptor(() => shadowLocation.search, (value) => {
|
|
3285
|
+
const targetPath = shadowLocation.pathname + ('?' + value).replace(/^\?+/, '?') + shadowLocation.hash;
|
|
3286
|
+
handleForPathNameAndSearch(targetPath, 'search');
|
|
3287
|
+
}),
|
|
3288
|
+
hash: createPropertyDescriptor(() => shadowLocation.hash, (value) => {
|
|
3289
|
+
const targetPath = shadowLocation.pathname + shadowLocation.search + ('#' + value).replace(/^#+/, '#');
|
|
3290
|
+
const targetLocation = createURL(targetPath, url);
|
|
3291
|
+
// The same hash will not trigger popStateEvent
|
|
3292
|
+
if (targetLocation.hash !== shadowLocation.hash) {
|
|
3293
|
+
navigateWithNativeEvent('pushState', setMicroPathToURL(appName, targetLocation), false);
|
|
3294
|
+
}
|
|
3295
|
+
}),
|
|
3296
|
+
fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
|
|
3297
|
+
});
|
|
3298
|
+
const createLocationMethod = (locationMethodName) => {
|
|
3299
|
+
return function (value) {
|
|
3300
|
+
const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
|
|
3301
|
+
if (targetPath)
|
|
3302
|
+
rawLocation[locationMethodName](targetPath);
|
|
3303
|
+
};
|
|
3304
|
+
};
|
|
3305
|
+
return assign(microLocation, {
|
|
3306
|
+
assign: createLocationMethod('assign'),
|
|
3307
|
+
replace: createLocationMethod('replace'),
|
|
3308
|
+
reload: (forcedReload) => rawLocation.reload(forcedReload),
|
|
3309
|
+
shadowLocation,
|
|
3310
|
+
});
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3313
|
+
/**
|
|
3314
|
+
* The router system has two operations: read and write
|
|
3315
|
+
* Read through location and write through history & location
|
|
3316
|
+
* @param appName app name
|
|
3317
|
+
* @param url app url
|
|
3318
|
+
* @returns MicroRouter
|
|
3319
|
+
*/
|
|
3320
|
+
function createMicroRouter(appName, url) {
|
|
3321
|
+
rewriteHistoryState();
|
|
3322
|
+
const microLocation = createMicroLocation(appName, url);
|
|
3323
|
+
return {
|
|
3324
|
+
microLocation,
|
|
3325
|
+
microHistory: createMicroHistory(appName, microLocation),
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
// 当沙箱执行start, 或者隐藏的keep-alive应用重新渲染时时才根据浏览器url更新location 或者 将参数更新到url上
|
|
3329
|
+
function initRouteStateWithURL(appName, microLocation, defaultPage) {
|
|
3330
|
+
const microPath = getMicroPathFromURL(appName);
|
|
3331
|
+
if (microPath) {
|
|
3332
|
+
updateMicroLocation(appName, microPath, microLocation, 'auto');
|
|
3333
|
+
}
|
|
3334
|
+
else {
|
|
3335
|
+
updateBrowserURLWithLocation(appName, microLocation, defaultPage);
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* initialize browser information according to microLocation
|
|
3340
|
+
* called on sandbox.start or reshow of keep-alive app
|
|
3341
|
+
*/
|
|
3342
|
+
function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
|
|
3343
|
+
// update microLocation with defaultPage
|
|
3344
|
+
if (defaultPage)
|
|
3345
|
+
updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
|
|
3346
|
+
// attach microApp route info to browser URL
|
|
3347
|
+
updateBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, globalEnv.rawWindow.history.state, null));
|
|
3348
|
+
// trigger guards after change browser URL
|
|
3349
|
+
autoTriggerNavigationGuard(appName, microLocation);
|
|
3350
|
+
}
|
|
3351
|
+
/**
|
|
3352
|
+
* In any case, microPath & microState will be removed from browser, but location will be initialized only when keep-router-state is false
|
|
3353
|
+
* @param appName app name
|
|
3354
|
+
* @param url app url
|
|
3355
|
+
* @param microLocation location of microApp
|
|
3356
|
+
* @param keepRouteState keep-router-state is only used to control whether to clear the location of microApp
|
|
3357
|
+
*/
|
|
3358
|
+
function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
|
|
3359
|
+
if (!keepRouteState) {
|
|
3360
|
+
const { pathname, search, hash } = createURL(url);
|
|
3361
|
+
updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
|
|
3362
|
+
}
|
|
3363
|
+
removeStateAndPathFromBrowser(appName);
|
|
3364
|
+
clearRouterWhenUnmount(appName);
|
|
3365
|
+
}
|
|
3366
|
+
/**
|
|
3367
|
+
* remove microState from history.state and remove microPath from browserURL
|
|
3368
|
+
* called on sandbox.stop or hidden of keep-alive app
|
|
3369
|
+
*/
|
|
3370
|
+
function removeStateAndPathFromBrowser(appName) {
|
|
3371
|
+
updateBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
/**
|
|
3375
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/fetch
|
|
3376
|
+
* Promise<Response> fetch(input[, init])
|
|
3377
|
+
* input: string/Request
|
|
3378
|
+
* init?: object
|
|
3379
|
+
* @param url app url
|
|
3380
|
+
* @param target proxy target
|
|
3381
|
+
*/
|
|
3382
|
+
function createMicroFetch(url, target) {
|
|
3383
|
+
if (!isUndefined(target) && !isFunction(target))
|
|
3384
|
+
return target;
|
|
3385
|
+
const rawFetch = target || globalEnv.rawWindow.fetch;
|
|
3386
|
+
return function microFetch(input, init, ...rests) {
|
|
3387
|
+
if (isString(input) || isURL(input)) {
|
|
3388
|
+
input = createURL(input, url).toString();
|
|
3389
|
+
}
|
|
3390
|
+
return rawFetch.call(globalEnv.rawWindow, input, init, ...rests);
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3393
|
+
/**
|
|
3394
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
|
|
3395
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
|
|
3396
|
+
* @param url app url
|
|
3397
|
+
* @param target proxy target
|
|
3398
|
+
*/
|
|
3399
|
+
function createMicroXMLHttpRequest(url, target) {
|
|
3400
|
+
if (!isUndefined(target) && !isFunction(target))
|
|
3401
|
+
return target;
|
|
3402
|
+
const rawXMLHttpRequest = target || globalEnv.rawWindow.XMLHttpRequest;
|
|
3403
|
+
return class MicroXMLHttpRequest extends rawXMLHttpRequest {
|
|
3404
|
+
open(method, reqUrl, ...rests) {
|
|
3405
|
+
if ((isString(reqUrl) && !/^f(ile|tp):\/\//.test(reqUrl)) || isURL(reqUrl)) {
|
|
3406
|
+
reqUrl = createURL(reqUrl, url).toString();
|
|
3407
|
+
}
|
|
3408
|
+
super.open(method, reqUrl, ...rests);
|
|
3409
|
+
}
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3412
|
+
function useMicroEventSource() {
|
|
3413
|
+
let eventSourceMap;
|
|
3414
|
+
/**
|
|
3415
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/EventSource
|
|
3416
|
+
* pc = new EventSource(url[, configuration])
|
|
3417
|
+
* url: string/Request
|
|
3418
|
+
* configuration?: object
|
|
3419
|
+
* @param url app url
|
|
3420
|
+
* @param target proxy target
|
|
3421
|
+
*/
|
|
3422
|
+
function createMicroEventSource(appName, url, target) {
|
|
3423
|
+
if (!isUndefined(target) && !isFunction(target))
|
|
3424
|
+
return target;
|
|
3425
|
+
const rawEventSource = target || globalEnv.rawWindow.EventSource;
|
|
3426
|
+
return class MicroEventSource extends rawEventSource {
|
|
3427
|
+
constructor(eventSourceUrl, eventSourceInitDict, ...rests) {
|
|
3428
|
+
if (isString(eventSourceUrl) || isURL(eventSourceUrl)) {
|
|
3429
|
+
eventSourceUrl = createURL(eventSourceUrl, url).toString();
|
|
3430
|
+
}
|
|
3431
|
+
super(eventSourceUrl, eventSourceInitDict, ...rests);
|
|
3432
|
+
if (eventSourceMap) {
|
|
3433
|
+
const eventSourceList = eventSourceMap.get(appName);
|
|
3434
|
+
if (eventSourceList) {
|
|
3435
|
+
eventSourceList.add(this);
|
|
3436
|
+
}
|
|
3437
|
+
else {
|
|
3438
|
+
eventSourceMap.set(appName, new Set([this]));
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
else {
|
|
3442
|
+
eventSourceMap = new Map([[appName, new Set([this])]]);
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
close() {
|
|
3446
|
+
var _a;
|
|
3447
|
+
super.close();
|
|
3448
|
+
(_a = eventSourceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.delete(this);
|
|
3449
|
+
}
|
|
3450
|
+
};
|
|
3451
|
+
}
|
|
3452
|
+
function clearMicroEventSource(appName) {
|
|
3453
|
+
const eventSourceList = eventSourceMap === null || eventSourceMap === void 0 ? void 0 : eventSourceMap.get(appName);
|
|
3454
|
+
if (eventSourceList === null || eventSourceList === void 0 ? void 0 : eventSourceList.size) {
|
|
3455
|
+
eventSourceList.forEach(item => {
|
|
3456
|
+
item.close();
|
|
3457
|
+
});
|
|
3458
|
+
eventSourceList.clear();
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
return {
|
|
3462
|
+
createMicroEventSource,
|
|
3463
|
+
clearMicroEventSource,
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
|
|
3468
|
+
const globalPropertyList = ['window', 'self', 'globalThis'];
|
|
3469
|
+
class SandBox {
|
|
3470
|
+
constructor(appName, url, useMemoryRouter = true) {
|
|
3471
|
+
/**
|
|
3472
|
+
* Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
|
|
3473
|
+
* Fix https://github.com/micro-zoe/micro-app/issues/234
|
|
3474
|
+
*/
|
|
3475
|
+
this.scopeProperties = [];
|
|
3476
|
+
// Properties that can be escape to rawWindow
|
|
3477
|
+
this.escapeProperties = [];
|
|
3478
|
+
// Properties newly added to microAppWindow
|
|
3479
|
+
this.injectedKeys = new Set();
|
|
3480
|
+
// Properties escape to rawWindow, cleared when unmount
|
|
3481
|
+
this.escapeKeys = new Set();
|
|
3482
|
+
// sandbox state
|
|
3483
|
+
this.active = false;
|
|
3484
|
+
this.microAppWindow = {}; // Proxy target
|
|
3485
|
+
this.adapter = new Adapter();
|
|
3486
|
+
// get scopeProperties and escapeProperties from plugins
|
|
3487
|
+
this.getSpecialProperties(appName);
|
|
3488
|
+
// create proxyWindow with Proxy(microAppWindow)
|
|
3489
|
+
this.proxyWindow = this.createProxyWindow(appName);
|
|
3490
|
+
// Rewrite global event listener & timeout
|
|
3491
|
+
assign(this, effect(appName, this.microAppWindow));
|
|
3492
|
+
// inject global properties
|
|
3493
|
+
this.initStaticGlobalKeys(this.microAppWindow, appName, url, useMemoryRouter);
|
|
3494
|
+
}
|
|
3495
|
+
start(baseRoute, useMemoryRouter = true, defaultPage = '') {
|
|
3496
|
+
if (!this.active) {
|
|
3497
|
+
this.active = true;
|
|
3498
|
+
if (useMemoryRouter) {
|
|
3499
|
+
this.initRouteState(defaultPage);
|
|
3500
|
+
// unique listener of popstate event for sub app
|
|
3501
|
+
this.removeHistoryListener = addHistoryListener(this.proxyWindow.__MICRO_APP_NAME__);
|
|
3502
|
+
}
|
|
3503
|
+
else {
|
|
3504
|
+
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
|
|
3505
|
+
}
|
|
3506
|
+
// prevent the key deleted during sandBox.stop after rewrite
|
|
3507
|
+
this.initGlobalKeysWhenStart(this.microAppWindow, this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__);
|
|
3508
|
+
if (++SandBox.activeCount === 1) {
|
|
3509
|
+
effectDocumentEvent();
|
|
3510
|
+
patchElementPrototypeMethods();
|
|
3511
|
+
listenUmountOfNestedApp();
|
|
3512
|
+
}
|
|
3513
|
+
fixBabelPolyfill6();
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
stop(keepRouteState, clearEventSource) {
|
|
3517
|
+
if (this.active) {
|
|
3518
|
+
this.releaseEffect();
|
|
3519
|
+
this.microAppWindow.microApp.clearDataListener();
|
|
3520
|
+
this.microAppWindow.microApp.clearGlobalDataListener();
|
|
3521
|
+
if (this.removeHistoryListener) {
|
|
3522
|
+
this.clearRouteState(keepRouteState);
|
|
3523
|
+
// release listener of popstate
|
|
3524
|
+
this.removeHistoryListener();
|
|
3525
|
+
}
|
|
3526
|
+
if (clearEventSource) {
|
|
3527
|
+
clearMicroEventSource(this.proxyWindow.__MICRO_APP_NAME__);
|
|
3528
|
+
}
|
|
3529
|
+
/**
|
|
3530
|
+
* NOTE:
|
|
3531
|
+
* 1. injectedKeys and escapeKeys must be placed at the back
|
|
3532
|
+
* 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
|
|
3533
|
+
*/
|
|
3534
|
+
this.injectedKeys.forEach((key) => {
|
|
3535
|
+
Reflect.deleteProperty(this.microAppWindow, key);
|
|
3536
|
+
});
|
|
3537
|
+
this.injectedKeys.clear();
|
|
3538
|
+
this.escapeKeys.forEach((key) => {
|
|
3539
|
+
Reflect.deleteProperty(globalEnv.rawWindow, key);
|
|
3540
|
+
});
|
|
3541
|
+
this.escapeKeys.clear();
|
|
3542
|
+
if (--SandBox.activeCount === 0) {
|
|
3543
|
+
releaseEffectDocumentEvent();
|
|
3544
|
+
releasePatches();
|
|
3545
|
+
}
|
|
3546
|
+
this.active = false;
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
// record umd snapshot before the first execution of umdHookMount
|
|
3550
|
+
recordUmdSnapshot() {
|
|
3551
|
+
this.microAppWindow.__MICRO_APP_UMD_MODE__ = true;
|
|
3552
|
+
this.recordUmdEffect();
|
|
3553
|
+
recordDataCenterSnapshot(this.microAppWindow.microApp);
|
|
3554
|
+
this.recordUmdInjectedValues = new Map();
|
|
3555
|
+
this.injectedKeys.forEach((key) => {
|
|
3556
|
+
this.recordUmdInjectedValues.set(key, Reflect.get(this.microAppWindow, key));
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
// rebuild umd snapshot before remount umd app
|
|
3560
|
+
rebuildUmdSnapshot() {
|
|
3561
|
+
this.recordUmdInjectedValues.forEach((value, key) => {
|
|
3562
|
+
Reflect.set(this.proxyWindow, key, value);
|
|
3563
|
+
});
|
|
3564
|
+
this.rebuildUmdEffect();
|
|
3565
|
+
rebuildDataCenterSnapshot(this.microAppWindow.microApp);
|
|
3566
|
+
}
|
|
3567
|
+
/**
|
|
3568
|
+
* get scopeProperties and escapeProperties from plugins & adapter
|
|
3569
|
+
* @param appName app name
|
|
3570
|
+
*/
|
|
3571
|
+
getSpecialProperties(appName) {
|
|
3572
|
+
var _a;
|
|
3573
|
+
this.scopeProperties = this.scopeProperties.concat(this.adapter.staticScopeProperties);
|
|
3574
|
+
if (isPlainObject(microApp.plugins)) {
|
|
3575
|
+
this.commonActionForSpecialProperties(microApp.plugins.global);
|
|
3576
|
+
this.commonActionForSpecialProperties((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
// common action for global plugins and module plugins
|
|
3580
|
+
commonActionForSpecialProperties(plugins) {
|
|
3581
|
+
if (isArray(plugins)) {
|
|
3582
|
+
for (const plugin of plugins) {
|
|
3583
|
+
if (isPlainObject(plugin)) {
|
|
3584
|
+
if (isArray(plugin.scopeProperties)) {
|
|
3585
|
+
this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
|
|
3586
|
+
}
|
|
3587
|
+
if (isArray(plugin.escapeProperties)) {
|
|
3588
|
+
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
// create proxyWindow with Proxy(microAppWindow)
|
|
3595
|
+
createProxyWindow(appName) {
|
|
3596
|
+
const rawWindow = globalEnv.rawWindow;
|
|
3597
|
+
const descriptorTargetMap = new Map();
|
|
3598
|
+
// window.xxx will trigger proxy
|
|
3599
|
+
return new Proxy(this.microAppWindow, {
|
|
3600
|
+
get: (target, key) => {
|
|
3601
|
+
throttleDeferForSetAppName(appName);
|
|
2558
3602
|
if (Reflect.has(target, key) ||
|
|
2559
3603
|
(isString(key) && /^__MICRO_APP_/.test(key)) ||
|
|
2560
3604
|
this.scopeProperties.includes(key))
|
|
@@ -2564,7 +3608,7 @@ class SandBox {
|
|
|
2564
3608
|
},
|
|
2565
3609
|
set: (target, key, value) => {
|
|
2566
3610
|
if (this.active) {
|
|
2567
|
-
if (escapeSetterKeyList.includes(key)) {
|
|
3611
|
+
if (this.adapter.escapeSetterKeyList.includes(key)) {
|
|
2568
3612
|
Reflect.set(rawWindow, key, value);
|
|
2569
3613
|
}
|
|
2570
3614
|
else if (
|
|
@@ -2588,7 +3632,8 @@ class SandBox {
|
|
|
2588
3632
|
this.injectedKeys.add(key);
|
|
2589
3633
|
}
|
|
2590
3634
|
if ((this.escapeProperties.includes(key) ||
|
|
2591
|
-
(staticEscapeProperties.includes(key) &&
|
|
3635
|
+
(this.adapter.staticEscapeProperties.includes(key) &&
|
|
3636
|
+
!Reflect.has(rawWindow, key))) &&
|
|
2592
3637
|
!this.scopeProperties.includes(key)) {
|
|
2593
3638
|
Reflect.set(rawWindow, key, value);
|
|
2594
3639
|
this.escapeKeys.add(key);
|
|
@@ -2644,21 +3689,35 @@ class SandBox {
|
|
|
2644
3689
|
* @param microAppWindow micro window
|
|
2645
3690
|
* @param appName app name
|
|
2646
3691
|
* @param url app url
|
|
3692
|
+
* @param useMemoryRouter whether use memory router
|
|
2647
3693
|
*/
|
|
2648
|
-
|
|
3694
|
+
initStaticGlobalKeys(microAppWindow, appName, url, useMemoryRouter) {
|
|
2649
3695
|
microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
|
|
2650
3696
|
microAppWindow.__MICRO_APP_NAME__ = appName;
|
|
3697
|
+
microAppWindow.__MICRO_APP_URL__ = url;
|
|
2651
3698
|
microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
|
|
2652
3699
|
microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
|
|
2653
|
-
microAppWindow.
|
|
3700
|
+
microAppWindow.rawWindow = globalEnv.rawWindow;
|
|
3701
|
+
microAppWindow.rawDocument = globalEnv.rawDocument;
|
|
3702
|
+
microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
|
|
2654
3703
|
removeDomScope,
|
|
2655
3704
|
pureCreateElement,
|
|
3705
|
+
router,
|
|
2656
3706
|
});
|
|
2657
|
-
microAppWindow.rawWindow = globalEnv.rawWindow;
|
|
2658
|
-
microAppWindow.rawDocument = globalEnv.rawDocument;
|
|
2659
|
-
microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
|
|
2660
3707
|
this.setMappingPropertiesWithRawDescriptor(microAppWindow);
|
|
3708
|
+
if (useMemoryRouter)
|
|
3709
|
+
this.setMicroAppRouter(microAppWindow, appName, url);
|
|
3710
|
+
}
|
|
3711
|
+
/**
|
|
3712
|
+
* init global properties of microAppWindow when exec sandBox.start
|
|
3713
|
+
* @param microAppWindow micro window
|
|
3714
|
+
* @param appName app name
|
|
3715
|
+
* @param url app url
|
|
3716
|
+
*/
|
|
3717
|
+
initGlobalKeysWhenStart(microAppWindow, appName, url) {
|
|
3718
|
+
microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
|
|
2661
3719
|
this.setHijackProperties(microAppWindow, appName);
|
|
3720
|
+
this.patchHijackRequest(microAppWindow, appName, url);
|
|
2662
3721
|
}
|
|
2663
3722
|
// properties associated with the native window
|
|
2664
3723
|
setMappingPropertiesWithRawDescriptor(microAppWindow) {
|
|
@@ -2692,14 +3751,16 @@ class SandBox {
|
|
|
2692
3751
|
let modifiedEval, modifiedImage;
|
|
2693
3752
|
rawDefineProperties(microAppWindow, {
|
|
2694
3753
|
document: {
|
|
3754
|
+
configurable: true,
|
|
3755
|
+
enumerable: true,
|
|
2695
3756
|
get() {
|
|
2696
3757
|
throttleDeferForSetAppName(appName);
|
|
2697
3758
|
return globalEnv.rawDocument;
|
|
2698
3759
|
},
|
|
2699
|
-
configurable: false,
|
|
2700
|
-
enumerable: true,
|
|
2701
3760
|
},
|
|
2702
3761
|
eval: {
|
|
3762
|
+
configurable: true,
|
|
3763
|
+
enumerable: false,
|
|
2703
3764
|
get() {
|
|
2704
3765
|
throttleDeferForSetAppName(appName);
|
|
2705
3766
|
return modifiedEval || eval;
|
|
@@ -2707,10 +3768,10 @@ class SandBox {
|
|
|
2707
3768
|
set: (value) => {
|
|
2708
3769
|
modifiedEval = value;
|
|
2709
3770
|
},
|
|
2710
|
-
configurable: true,
|
|
2711
|
-
enumerable: false,
|
|
2712
3771
|
},
|
|
2713
3772
|
Image: {
|
|
3773
|
+
configurable: true,
|
|
3774
|
+
enumerable: false,
|
|
2714
3775
|
get() {
|
|
2715
3776
|
throttleDeferForSetAppName(appName);
|
|
2716
3777
|
return modifiedImage || globalEnv.ImageProxy;
|
|
@@ -2718,11 +3779,82 @@ class SandBox {
|
|
|
2718
3779
|
set: (value) => {
|
|
2719
3780
|
modifiedImage = value;
|
|
2720
3781
|
},
|
|
3782
|
+
},
|
|
3783
|
+
});
|
|
3784
|
+
}
|
|
3785
|
+
// rewrite fetch, XMLHttpRequest, EventSource
|
|
3786
|
+
patchHijackRequest(microAppWindow, appName, url) {
|
|
3787
|
+
let microFetch = createMicroFetch(url);
|
|
3788
|
+
let microXMLHttpRequest = createMicroXMLHttpRequest(url);
|
|
3789
|
+
let microEventSource = createMicroEventSource(appName, url);
|
|
3790
|
+
rawDefineProperties(microAppWindow, {
|
|
3791
|
+
fetch: {
|
|
2721
3792
|
configurable: true,
|
|
2722
|
-
enumerable:
|
|
3793
|
+
enumerable: true,
|
|
3794
|
+
get() {
|
|
3795
|
+
return microFetch;
|
|
3796
|
+
},
|
|
3797
|
+
set(value) {
|
|
3798
|
+
microFetch = createMicroFetch(url, value);
|
|
3799
|
+
},
|
|
3800
|
+
},
|
|
3801
|
+
XMLHttpRequest: {
|
|
3802
|
+
configurable: true,
|
|
3803
|
+
enumerable: true,
|
|
3804
|
+
get() {
|
|
3805
|
+
return microXMLHttpRequest;
|
|
3806
|
+
},
|
|
3807
|
+
set(value) {
|
|
3808
|
+
microXMLHttpRequest = createMicroXMLHttpRequest(url, value);
|
|
3809
|
+
},
|
|
3810
|
+
},
|
|
3811
|
+
EventSource: {
|
|
3812
|
+
configurable: true,
|
|
3813
|
+
enumerable: true,
|
|
3814
|
+
get() {
|
|
3815
|
+
return microEventSource;
|
|
3816
|
+
},
|
|
3817
|
+
set(value) {
|
|
3818
|
+
microEventSource = createMicroEventSource(appName, url, value);
|
|
3819
|
+
},
|
|
3820
|
+
},
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3823
|
+
// set location & history for memory router
|
|
3824
|
+
setMicroAppRouter(microAppWindow, appName, url) {
|
|
3825
|
+
const { microLocation, microHistory } = createMicroRouter(appName, url);
|
|
3826
|
+
rawDefineProperties(microAppWindow, {
|
|
3827
|
+
location: {
|
|
3828
|
+
configurable: false,
|
|
3829
|
+
enumerable: true,
|
|
3830
|
+
get() {
|
|
3831
|
+
return microLocation;
|
|
3832
|
+
},
|
|
3833
|
+
set: (value) => {
|
|
3834
|
+
globalEnv.rawWindow.location = value;
|
|
3835
|
+
},
|
|
3836
|
+
},
|
|
3837
|
+
history: {
|
|
3838
|
+
configurable: true,
|
|
3839
|
+
enumerable: true,
|
|
3840
|
+
get() {
|
|
3841
|
+
return microHistory;
|
|
3842
|
+
},
|
|
2723
3843
|
},
|
|
2724
3844
|
});
|
|
2725
3845
|
}
|
|
3846
|
+
initRouteState(defaultPage) {
|
|
3847
|
+
initRouteStateWithURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location, defaultPage);
|
|
3848
|
+
}
|
|
3849
|
+
clearRouteState(keepRouteState) {
|
|
3850
|
+
clearRouteStateFromURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, this.proxyWindow.location, keepRouteState);
|
|
3851
|
+
}
|
|
3852
|
+
setRouteInfoForKeepAliveApp() {
|
|
3853
|
+
updateBrowserURLWithLocation(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location);
|
|
3854
|
+
}
|
|
3855
|
+
removeRouteInfoForKeepAliveApp() {
|
|
3856
|
+
removeStateAndPathFromBrowser(this.proxyWindow.__MICRO_APP_NAME__);
|
|
3857
|
+
}
|
|
2726
3858
|
}
|
|
2727
3859
|
SandBox.activeCount = 0; // number of active sandbox
|
|
2728
3860
|
|
|
@@ -2756,7 +3888,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
|
|
|
2756
3888
|
element = getRootContainer(element);
|
|
2757
3889
|
// clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
|
|
2758
3890
|
removeDomScope();
|
|
2759
|
-
const detail =
|
|
3891
|
+
const detail = assign({
|
|
2760
3892
|
name: appName,
|
|
2761
3893
|
container: element,
|
|
2762
3894
|
}, error && {
|
|
@@ -2781,7 +3913,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
|
|
|
2781
3913
|
* @param detail event detail
|
|
2782
3914
|
*/
|
|
2783
3915
|
function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
|
|
2784
|
-
const event = new CustomEvent(
|
|
3916
|
+
const event = new CustomEvent(formatEventName$1(eventName, appName), {
|
|
2785
3917
|
detail,
|
|
2786
3918
|
});
|
|
2787
3919
|
window.dispatchEvent(event);
|
|
@@ -2790,8 +3922,8 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
|
|
|
2790
3922
|
// micro app instances
|
|
2791
3923
|
const appInstanceMap = new Map();
|
|
2792
3924
|
class CreateApp {
|
|
2793
|
-
constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, baseroute, }) {
|
|
2794
|
-
this.state = appStates.
|
|
3925
|
+
constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, defaultPage, }) {
|
|
3926
|
+
this.state = appStates.CREATED;
|
|
2795
3927
|
this.keepAliveState = null;
|
|
2796
3928
|
this.keepAliveContainer = null;
|
|
2797
3929
|
this.loadSourceLevel = 0;
|
|
@@ -2802,27 +3934,29 @@ class CreateApp {
|
|
|
2802
3934
|
this.isPrefetch = false;
|
|
2803
3935
|
this.prefetchResolve = null;
|
|
2804
3936
|
this.container = null;
|
|
2805
|
-
this.baseroute = '';
|
|
2806
3937
|
this.sandBox = null;
|
|
2807
3938
|
this.container = container !== null && container !== void 0 ? container : null;
|
|
2808
3939
|
this.inline = inline !== null && inline !== void 0 ? inline : false;
|
|
2809
3940
|
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
|
|
3941
|
+
this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : false;
|
|
2810
3942
|
this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
|
|
2811
3943
|
// optional during init👆
|
|
2812
3944
|
this.name = name;
|
|
2813
3945
|
this.url = url;
|
|
2814
3946
|
this.useSandbox = useSandbox;
|
|
2815
3947
|
this.scopecss = this.useSandbox && scopecss;
|
|
3948
|
+
this.useMemoryRouter = this.useSandbox && useMemoryRouter;
|
|
3949
|
+
this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : '';
|
|
2816
3950
|
this.source = {
|
|
2817
3951
|
links: new Map(),
|
|
2818
3952
|
scripts: new Map(),
|
|
2819
3953
|
};
|
|
2820
3954
|
this.loadSourceCode();
|
|
2821
|
-
this.useSandbox && (this.sandBox = new SandBox(name, url));
|
|
3955
|
+
this.useSandbox && (this.sandBox = new SandBox(name, url, this.useMemoryRouter));
|
|
2822
3956
|
}
|
|
2823
3957
|
// Load resources
|
|
2824
3958
|
loadSourceCode() {
|
|
2825
|
-
this.state = appStates.
|
|
3959
|
+
this.state = appStates.LOADING;
|
|
2826
3960
|
extractHtml(this);
|
|
2827
3961
|
}
|
|
2828
3962
|
/**
|
|
@@ -2837,7 +3971,7 @@ class CreateApp {
|
|
|
2837
3971
|
this.prefetchResolve = null;
|
|
2838
3972
|
}
|
|
2839
3973
|
else if (appStates.UNMOUNT !== this.state) {
|
|
2840
|
-
this.state = appStates.
|
|
3974
|
+
this.state = appStates.LOADED;
|
|
2841
3975
|
this.mount();
|
|
2842
3976
|
}
|
|
2843
3977
|
}
|
|
@@ -2854,7 +3988,7 @@ class CreateApp {
|
|
|
2854
3988
|
}
|
|
2855
3989
|
if (appStates.UNMOUNT !== this.state) {
|
|
2856
3990
|
this.onerror(e);
|
|
2857
|
-
this.state = appStates.
|
|
3991
|
+
this.state = appStates.LOAD_FAILED;
|
|
2858
3992
|
}
|
|
2859
3993
|
}
|
|
2860
3994
|
/**
|
|
@@ -2862,22 +3996,26 @@ class CreateApp {
|
|
|
2862
3996
|
* @param container app container
|
|
2863
3997
|
* @param inline js runs in inline mode
|
|
2864
3998
|
* @param baseroute route prefix, default is ''
|
|
3999
|
+
* @param keepRouteState keep route state when unmount, default is false
|
|
2865
4000
|
*/
|
|
2866
|
-
mount(container, inline, baseroute) {
|
|
4001
|
+
mount(container, inline, baseroute, keepRouteState, defaultPage) {
|
|
2867
4002
|
var _a, _b, _c;
|
|
2868
|
-
if (isBoolean(inline)
|
|
4003
|
+
if (isBoolean(inline))
|
|
2869
4004
|
this.inline = inline;
|
|
2870
|
-
|
|
4005
|
+
// keepRouteState effective on unmount
|
|
4006
|
+
if (isBoolean(keepRouteState))
|
|
4007
|
+
this.keepRouteState = keepRouteState;
|
|
2871
4008
|
this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
|
|
2872
4009
|
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
|
|
4010
|
+
this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : this.defaultPage;
|
|
2873
4011
|
if (this.loadSourceLevel !== 2) {
|
|
2874
|
-
this.state = appStates.
|
|
4012
|
+
this.state = appStates.LOADING;
|
|
2875
4013
|
return;
|
|
2876
4014
|
}
|
|
2877
4015
|
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
|
|
2878
4016
|
this.state = appStates.MOUNTING;
|
|
2879
4017
|
cloneContainer(this.source.html, this.container, !this.umdMode);
|
|
2880
|
-
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
|
|
4018
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute, this.useMemoryRouter, this.defaultPage);
|
|
2881
4019
|
let umdHookMountResult; // result of mount function
|
|
2882
4020
|
if (!this.umdMode) {
|
|
2883
4021
|
let hasDispatchMountedEvent = false;
|
|
@@ -2942,11 +4080,12 @@ class CreateApp {
|
|
|
2942
4080
|
}
|
|
2943
4081
|
/**
|
|
2944
4082
|
* unmount app
|
|
4083
|
+
* NOTE: Do not add any params on account of unmountApp
|
|
2945
4084
|
* @param destroy completely destroy, delete cache resources
|
|
2946
4085
|
* @param unmountcb callback of unmount
|
|
2947
4086
|
*/
|
|
2948
4087
|
unmount(destroy, unmountcb) {
|
|
2949
|
-
if (this.state === appStates.
|
|
4088
|
+
if (this.state === appStates.LOAD_FAILED) {
|
|
2950
4089
|
destroy = true;
|
|
2951
4090
|
}
|
|
2952
4091
|
this.state = appStates.UNMOUNT;
|
|
@@ -2999,8 +4138,13 @@ class CreateApp {
|
|
|
2999
4138
|
else if (this.umdMode && this.container.childElementCount) {
|
|
3000
4139
|
cloneContainer(this.container, this.source.html, false);
|
|
3001
4140
|
}
|
|
3002
|
-
|
|
3003
|
-
|
|
4141
|
+
/**
|
|
4142
|
+
* this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
|
|
4143
|
+
* NOTE:
|
|
4144
|
+
* 1. if destroy is true, clear route state
|
|
4145
|
+
* 2. umd mode and keep-alive will not clear EventSource
|
|
4146
|
+
*/
|
|
4147
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop(this.keepRouteState && !destroy, !this.umdMode || destroy);
|
|
3004
4148
|
if (!getActiveApps().length) {
|
|
3005
4149
|
releasePatchSetAttribute();
|
|
3006
4150
|
}
|
|
@@ -3019,34 +4163,40 @@ class CreateApp {
|
|
|
3019
4163
|
}
|
|
3020
4164
|
// hidden app when disconnectedCallback called with keep-alive
|
|
3021
4165
|
hiddenKeepAliveApp() {
|
|
4166
|
+
var _a;
|
|
3022
4167
|
const oldContainer = this.container;
|
|
3023
4168
|
cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
|
|
3024
4169
|
this.container = this.keepAliveContainer;
|
|
3025
4170
|
this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
|
|
3026
4171
|
// event should dispatch before clone node
|
|
3027
|
-
// dispatch
|
|
4172
|
+
// dispatch afterHidden event to micro-app
|
|
3028
4173
|
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
3029
4174
|
appState: 'afterhidden',
|
|
3030
4175
|
});
|
|
3031
|
-
// dispatch
|
|
4176
|
+
// dispatch afterHidden event to base app
|
|
3032
4177
|
dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
|
|
4178
|
+
// called after lifeCyclesEvent
|
|
4179
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
|
|
3033
4180
|
}
|
|
3034
4181
|
// show app when connectedCallback called with keep-alive
|
|
3035
4182
|
showKeepAliveApp(container) {
|
|
3036
|
-
|
|
4183
|
+
var _a;
|
|
4184
|
+
// dispatch beforeShow event to micro-app
|
|
3037
4185
|
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
3038
4186
|
appState: 'beforeshow',
|
|
3039
4187
|
});
|
|
3040
|
-
// dispatch
|
|
4188
|
+
// dispatch beforeShow event to base app
|
|
3041
4189
|
dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
|
|
3042
4190
|
cloneContainer(this.container, container, false);
|
|
3043
4191
|
this.container = container;
|
|
3044
4192
|
this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
|
|
3045
|
-
//
|
|
4193
|
+
// called before lifeCyclesEvent
|
|
4194
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setRouteInfoForKeepAliveApp();
|
|
4195
|
+
// dispatch afterShow event to micro-app
|
|
3046
4196
|
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
3047
4197
|
appState: 'aftershow',
|
|
3048
4198
|
});
|
|
3049
|
-
// dispatch
|
|
4199
|
+
// dispatch afterShow event to base app
|
|
3050
4200
|
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
|
|
3051
4201
|
}
|
|
3052
4202
|
/**
|
|
@@ -3154,12 +4304,17 @@ function defineElement(tagName) {
|
|
|
3154
4304
|
}
|
|
3155
4305
|
disconnectedCallback() {
|
|
3156
4306
|
this.hasConnected = false;
|
|
3157
|
-
|
|
3158
|
-
if (
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
4307
|
+
const app = appInstanceMap.get(this.appName);
|
|
4308
|
+
if (app &&
|
|
4309
|
+
app.getAppState() !== appStates.UNMOUNT &&
|
|
4310
|
+
app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
|
|
4311
|
+
// keep-alive
|
|
4312
|
+
if (this.getKeepAliveModeResult()) {
|
|
4313
|
+
this.handleHiddenKeepAliveApp();
|
|
4314
|
+
}
|
|
4315
|
+
else {
|
|
4316
|
+
this.handleUnmount(this.getDestroyCompatibleResult());
|
|
4317
|
+
}
|
|
3163
4318
|
}
|
|
3164
4319
|
}
|
|
3165
4320
|
attributeChangedCallback(attr, _oldVal, newVal) {
|
|
@@ -3207,12 +4362,7 @@ function defineElement(tagName) {
|
|
|
3207
4362
|
if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
|
|
3208
4363
|
this.attachShadow({ mode: 'open' });
|
|
3209
4364
|
}
|
|
3210
|
-
|
|
3211
|
-
this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, this.appUrl);
|
|
3212
|
-
}
|
|
3213
|
-
else if (this.ssrUrl) {
|
|
3214
|
-
this.ssrUrl = '';
|
|
3215
|
-
}
|
|
4365
|
+
this.updateSsrUrl(this.appUrl);
|
|
3216
4366
|
if (appInstanceMap.has(this.appName)) {
|
|
3217
4367
|
const app = appInstanceMap.get(this.appName);
|
|
3218
4368
|
const existAppUrl = app.ssrUrl || app.url;
|
|
@@ -3246,15 +4396,9 @@ function defineElement(tagName) {
|
|
|
3246
4396
|
actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp) {
|
|
3247
4397
|
var _a;
|
|
3248
4398
|
/**
|
|
3249
|
-
* change ssrUrl in ssr mode
|
|
3250
4399
|
* do not add judgment of formatAttrUrl === this.appUrl
|
|
3251
4400
|
*/
|
|
3252
|
-
|
|
3253
|
-
this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
|
|
3254
|
-
}
|
|
3255
|
-
else if (this.ssrUrl) {
|
|
3256
|
-
this.ssrUrl = '';
|
|
3257
|
-
}
|
|
4401
|
+
this.updateSsrUrl(formatAttrUrl);
|
|
3258
4402
|
this.appName = formatAttrName;
|
|
3259
4403
|
this.appUrl = formatAttrUrl;
|
|
3260
4404
|
((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
|
|
@@ -3314,14 +4458,14 @@ function defineElement(tagName) {
|
|
|
3314
4458
|
app.isPrefetch = false;
|
|
3315
4459
|
defer(() => {
|
|
3316
4460
|
var _a;
|
|
3317
|
-
return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
|
|
4461
|
+
return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible(), this.getDisposeResult('keep-router-state'), this.getDefaultPageValue());
|
|
3318
4462
|
});
|
|
3319
4463
|
}
|
|
3320
4464
|
// create app instance
|
|
3321
4465
|
handleCreateApp() {
|
|
3322
4466
|
var _a;
|
|
3323
4467
|
/**
|
|
3324
|
-
* actions for
|
|
4468
|
+
* actions for destroy old app
|
|
3325
4469
|
* fix of unmounted umd app with disableSandbox
|
|
3326
4470
|
*/
|
|
3327
4471
|
if (appInstanceMap.has(this.appName)) {
|
|
@@ -3335,7 +4479,10 @@ function defineElement(tagName) {
|
|
|
3335
4479
|
inline: this.getDisposeResult('inline'),
|
|
3336
4480
|
scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
|
|
3337
4481
|
useSandbox: !this.getDisposeResult('disableSandbox'),
|
|
4482
|
+
useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
|
|
3338
4483
|
baseroute: this.getBaseRouteCompatible(),
|
|
4484
|
+
keepRouteState: this.getDisposeResult('keep-router-state'),
|
|
4485
|
+
defaultPage: this.getDefaultPageValue(),
|
|
3339
4486
|
});
|
|
3340
4487
|
appInstanceMap.set(this.appName, instance);
|
|
3341
4488
|
}
|
|
@@ -3343,11 +4490,12 @@ function defineElement(tagName) {
|
|
|
3343
4490
|
* unmount app
|
|
3344
4491
|
* @param destroy delete cache resources when unmount
|
|
3345
4492
|
*/
|
|
3346
|
-
handleUnmount(destroy,
|
|
4493
|
+
handleUnmount(destroy, unmountCb) {
|
|
3347
4494
|
const app = appInstanceMap.get(this.appName);
|
|
3348
4495
|
if (app &&
|
|
3349
|
-
app.getAppState() !== appStates.UNMOUNT)
|
|
3350
|
-
app.unmount(destroy,
|
|
4496
|
+
app.getAppState() !== appStates.UNMOUNT) {
|
|
4497
|
+
app.unmount(destroy, unmountCb);
|
|
4498
|
+
}
|
|
3351
4499
|
}
|
|
3352
4500
|
// hidden app when disconnectedCallback called with keep-alive
|
|
3353
4501
|
handleHiddenKeepAliveApp() {
|
|
@@ -3410,6 +4558,37 @@ function defineElement(tagName) {
|
|
|
3410
4558
|
getKeepAliveModeResult() {
|
|
3411
4559
|
return this.getDisposeResult('keep-alive') && !this.getDestroyCompatibleResult();
|
|
3412
4560
|
}
|
|
4561
|
+
/**
|
|
4562
|
+
* change ssrUrl in ssr mode
|
|
4563
|
+
*/
|
|
4564
|
+
updateSsrUrl(baseUrl) {
|
|
4565
|
+
if (this.getDisposeResult('ssr')) {
|
|
4566
|
+
if (this.getDisposeResult('disable-memory-router')) {
|
|
4567
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
4568
|
+
this.ssrUrl = CompletionPath(rawLocation.pathname + rawLocation.search, baseUrl);
|
|
4569
|
+
}
|
|
4570
|
+
else {
|
|
4571
|
+
// get path from browser URL
|
|
4572
|
+
let targetPath = getNoHashMicroPathFromURL(this.appName, baseUrl);
|
|
4573
|
+
const defaultPagePath = this.getDefaultPageValue();
|
|
4574
|
+
if (!targetPath && defaultPagePath) {
|
|
4575
|
+
const targetLocation = createURL(defaultPagePath, baseUrl);
|
|
4576
|
+
targetPath = targetLocation.origin + targetLocation.pathname + targetLocation.search;
|
|
4577
|
+
}
|
|
4578
|
+
this.ssrUrl = targetPath;
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
else if (this.ssrUrl) {
|
|
4582
|
+
this.ssrUrl = '';
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
/**
|
|
4586
|
+
* get config of default page
|
|
4587
|
+
*/
|
|
4588
|
+
getDefaultPageValue() {
|
|
4589
|
+
var _a, _b, _c;
|
|
4590
|
+
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 : '';
|
|
4591
|
+
}
|
|
3413
4592
|
/**
|
|
3414
4593
|
* Data from the base application
|
|
3415
4594
|
*/
|
|
@@ -3444,12 +4623,13 @@ function defineElement(tagName) {
|
|
|
3444
4623
|
* url: string,
|
|
3445
4624
|
* disableScopecss?: boolean,
|
|
3446
4625
|
* disableSandbox?: boolean,
|
|
4626
|
+
* disableMemoryRouter?: boolean,
|
|
3447
4627
|
* },
|
|
3448
4628
|
* ...
|
|
3449
4629
|
* ])
|
|
3450
4630
|
* Note:
|
|
3451
4631
|
* 1: preFetch is asynchronous and is performed only when the browser is idle
|
|
3452
|
-
* 2: disableScopecss, disableSandbox must be same with micro-app element, if conflict, the one who executes first shall prevail
|
|
4632
|
+
* 2: disableScopecss, disableSandbox, disableMemoryRouter must be same with micro-app element, if conflict, the one who executes first shall prevail
|
|
3453
4633
|
* @param apps micro apps
|
|
3454
4634
|
*/
|
|
3455
4635
|
function preFetch(apps) {
|
|
@@ -3467,7 +4647,7 @@ function preFetch(apps) {
|
|
|
3467
4647
|
function preFetchInSerial(prefetchApp) {
|
|
3468
4648
|
return new Promise((resolve) => {
|
|
3469
4649
|
requestIdleCallback(() => {
|
|
3470
|
-
var _a, _b;
|
|
4650
|
+
var _a, _b, _c;
|
|
3471
4651
|
if (isPlainObject(prefetchApp) && navigator.onLine) {
|
|
3472
4652
|
prefetchApp.name = formatAppName(prefetchApp.name);
|
|
3473
4653
|
prefetchApp.url = formatAppURL(prefetchApp.url, prefetchApp.name);
|
|
@@ -3477,6 +4657,7 @@ function preFetchInSerial(prefetchApp) {
|
|
|
3477
4657
|
url: prefetchApp.url,
|
|
3478
4658
|
scopecss: !((_a = prefetchApp.disableScopecss) !== null && _a !== void 0 ? _a : microApp.disableScopecss),
|
|
3479
4659
|
useSandbox: !((_b = prefetchApp.disableSandbox) !== null && _b !== void 0 ? _b : microApp.disableSandbox),
|
|
4660
|
+
useMemoryRouter: !((_c = prefetchApp.disableMemoryRouter) !== null && _c !== void 0 ? _c : microApp.disableMemoryRouter),
|
|
3480
4661
|
});
|
|
3481
4662
|
app.isPrefetch = true;
|
|
3482
4663
|
app.prefetchResolve = resolve;
|
|
@@ -3545,7 +4726,7 @@ function getAllApps() {
|
|
|
3545
4726
|
/**
|
|
3546
4727
|
* unmount app by appName
|
|
3547
4728
|
* @param appName
|
|
3548
|
-
* @param options
|
|
4729
|
+
* @param options unmountAppOptions
|
|
3549
4730
|
* @returns Promise<void>
|
|
3550
4731
|
*/
|
|
3551
4732
|
function unmountApp(appName, options) {
|
|
@@ -3619,6 +4800,7 @@ class MicroApp extends EventCenterForBaseApp {
|
|
|
3619
4800
|
super(...arguments);
|
|
3620
4801
|
this.tagName = 'micro-app';
|
|
3621
4802
|
this.preFetch = preFetch;
|
|
4803
|
+
this.router = router;
|
|
3622
4804
|
}
|
|
3623
4805
|
start(options) {
|
|
3624
4806
|
if (!isBrowser || !window.customElements) {
|
|
@@ -3648,6 +4830,7 @@ class MicroApp extends EventCenterForBaseApp {
|
|
|
3648
4830
|
this.inline = options.inline;
|
|
3649
4831
|
this.disableScopecss = options.disableScopecss;
|
|
3650
4832
|
this.disableSandbox = options.disableSandbox;
|
|
4833
|
+
this.disableMemoryRouter = options.disableMemoryRouter;
|
|
3651
4834
|
this.ssr = options.ssr;
|
|
3652
4835
|
isFunction(options.fetch) && (this.fetch = options.fetch);
|
|
3653
4836
|
isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);
|