@micro-zoe/micro-app 1.0.0-alpha.8 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/README.zh-cn.md +4 -3
- package/lib/index.d.ts +28 -9
- package/lib/index.esm.js +2189 -692
- 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 +3 -3
- package/typings/global.d.ts +53 -19
package/lib/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const version = '1.0.0-
|
|
1
|
+
const version = '1.0.0-beta.0';
|
|
2
2
|
// do not use isUndefined
|
|
3
3
|
const isBrowser = typeof window !== 'undefined';
|
|
4
4
|
// do not use isUndefined
|
|
@@ -7,7 +7,6 @@ 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
10
|
const noopFalse = () => false;
|
|
12
11
|
// Array.isArray
|
|
13
12
|
const isArray = Array.isArray;
|
|
@@ -74,13 +73,46 @@ function isShadowRoot(target) {
|
|
|
74
73
|
return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
|
|
75
74
|
}
|
|
76
75
|
function isURL(target) {
|
|
77
|
-
|
|
76
|
+
var _a;
|
|
77
|
+
return target instanceof URL || !!((_a = target) === null || _a === void 0 ? void 0 : _a.href);
|
|
78
78
|
}
|
|
79
|
+
// iframe element not instanceof base app Element, use tagName instead
|
|
79
80
|
function isElement(target) {
|
|
80
|
-
|
|
81
|
+
var _a;
|
|
82
|
+
return target instanceof Element || isString((_a = target) === null || _a === void 0 ? void 0 : _a.tagName);
|
|
81
83
|
}
|
|
84
|
+
// iframe node not instanceof base app Node, use nodeType instead
|
|
82
85
|
function isNode(target) {
|
|
83
|
-
|
|
86
|
+
var _a;
|
|
87
|
+
return target instanceof Node || isNumber((_a = target) === null || _a === void 0 ? void 0 : _a.nodeType);
|
|
88
|
+
}
|
|
89
|
+
function isLinkElement(target) {
|
|
90
|
+
var _a, _b;
|
|
91
|
+
return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'LINK';
|
|
92
|
+
}
|
|
93
|
+
function isStyleElement(target) {
|
|
94
|
+
var _a, _b;
|
|
95
|
+
return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'STYLE';
|
|
96
|
+
}
|
|
97
|
+
function isScriptElement(target) {
|
|
98
|
+
var _a, _b;
|
|
99
|
+
return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'SCRIPT';
|
|
100
|
+
}
|
|
101
|
+
function isIFrameElement(target) {
|
|
102
|
+
var _a, _b;
|
|
103
|
+
return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'IFRAME';
|
|
104
|
+
}
|
|
105
|
+
function isDivElement(target) {
|
|
106
|
+
var _a, _b;
|
|
107
|
+
return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'DIV';
|
|
108
|
+
}
|
|
109
|
+
function isImageElement(target) {
|
|
110
|
+
var _a, _b;
|
|
111
|
+
return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'IMG';
|
|
112
|
+
}
|
|
113
|
+
function isBaseElement(target) {
|
|
114
|
+
var _a, _b;
|
|
115
|
+
return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'BASE';
|
|
84
116
|
}
|
|
85
117
|
// is ProxyDocument
|
|
86
118
|
function isProxyDocument(target) {
|
|
@@ -323,6 +355,7 @@ function pureCreateElement(tagName, options) {
|
|
|
323
355
|
function cloneContainer(origin, target, deep) {
|
|
324
356
|
target.innerHTML = '';
|
|
325
357
|
if (deep) {
|
|
358
|
+
// TODO: ShadowRoot兼容,ShadowRoot不能直接使用cloneNode
|
|
326
359
|
const clonedNode = origin.cloneNode(true);
|
|
327
360
|
const fragment = document.createDocumentFragment();
|
|
328
361
|
Array.from(clonedNode.childNodes).forEach((node) => {
|
|
@@ -335,6 +368,7 @@ function cloneContainer(origin, target, deep) {
|
|
|
335
368
|
target.appendChild(node);
|
|
336
369
|
});
|
|
337
370
|
}
|
|
371
|
+
return target;
|
|
338
372
|
}
|
|
339
373
|
// is invalid key of querySelector
|
|
340
374
|
function isInvalidQuerySelectorKey(key) {
|
|
@@ -344,7 +378,8 @@ function isInvalidQuerySelectorKey(key) {
|
|
|
344
378
|
function isUniqueElement(key) {
|
|
345
379
|
return (/^body$/i.test(key) ||
|
|
346
380
|
/^head$/i.test(key) ||
|
|
347
|
-
/^html$/i.test(key)
|
|
381
|
+
/^html$/i.test(key) ||
|
|
382
|
+
/^title$/i.test(key));
|
|
348
383
|
}
|
|
349
384
|
/**
|
|
350
385
|
* get micro-app element
|
|
@@ -499,12 +534,17 @@ function isInlineScript(address) {
|
|
|
499
534
|
* @param appName app.name
|
|
500
535
|
* @param args arguments
|
|
501
536
|
*/
|
|
502
|
-
function
|
|
537
|
+
function execMicroAppGlobalHook(fn, appName, hookName, ...args) {
|
|
503
538
|
try {
|
|
504
539
|
isFunction(fn) && fn(...args);
|
|
505
540
|
}
|
|
506
541
|
catch (e) {
|
|
507
|
-
logError(`
|
|
542
|
+
logError(`An error occurred in app ${appName} window.${hookName} \n`, null, e);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function clearDOM($dom) {
|
|
546
|
+
while ($dom === null || $dom === void 0 ? void 0 : $dom.firstChild) {
|
|
547
|
+
$dom.removeChild($dom.firstChild);
|
|
508
548
|
}
|
|
509
549
|
}
|
|
510
550
|
|
|
@@ -543,6 +583,11 @@ var microGlobalEvent;
|
|
|
543
583
|
microGlobalEvent["ONMOUNT"] = "onmount";
|
|
544
584
|
microGlobalEvent["ONUNMOUNT"] = "onunmount";
|
|
545
585
|
})(microGlobalEvent || (microGlobalEvent = {}));
|
|
586
|
+
// custom event of child app
|
|
587
|
+
const microAppCustomEvent = [
|
|
588
|
+
'unmount',
|
|
589
|
+
'appstate-change',
|
|
590
|
+
];
|
|
546
591
|
// keep-alive status
|
|
547
592
|
var keepAliveStates;
|
|
548
593
|
(function (keepAliveStates) {
|
|
@@ -565,7 +610,6 @@ var MicroAppConfig;
|
|
|
565
610
|
MicroAppConfig["HIDDEN_ROUTER"] = "hidden-router";
|
|
566
611
|
MicroAppConfig["KEEP_ALIVE"] = "keep-alive";
|
|
567
612
|
MicroAppConfig["CLEAR_DATA"] = "clear-data";
|
|
568
|
-
MicroAppConfig["ESMODULE"] = "esmodule";
|
|
569
613
|
MicroAppConfig["SSR"] = "ssr";
|
|
570
614
|
MicroAppConfig["FIBER"] = "fiber";
|
|
571
615
|
})(MicroAppConfig || (MicroAppConfig = {}));
|
|
@@ -578,7 +622,7 @@ const PREFETCH_LEVEL = [1, 2, 3];
|
|
|
578
622
|
* NOTE:
|
|
579
623
|
* 1. Do not add fetch, XMLHttpRequest, EventSource
|
|
580
624
|
*/
|
|
581
|
-
const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,
|
|
625
|
+
const globalKeyToBeCached = 'window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history';
|
|
582
626
|
|
|
583
627
|
/**
|
|
584
628
|
* fetch source of html, js, css
|
|
@@ -1093,9 +1137,6 @@ function dispatchOnErrorEvent(element) {
|
|
|
1093
1137
|
function createSourceCenter() {
|
|
1094
1138
|
const linkList = new Map();
|
|
1095
1139
|
const scriptList = new Map();
|
|
1096
|
-
// setInterval(() => {
|
|
1097
|
-
// console.log(linkList, scriptList)
|
|
1098
|
-
// }, 10000);
|
|
1099
1140
|
function createSourceHandler(targetList) {
|
|
1100
1141
|
return {
|
|
1101
1142
|
setInfo(address, info) {
|
|
@@ -1137,7 +1178,7 @@ function getExistParseCode(appName, prefix, linkInfo) {
|
|
|
1137
1178
|
if (item !== appName) {
|
|
1138
1179
|
const appSpaceData = appSpace[item];
|
|
1139
1180
|
if (appSpaceData.parsedCode) {
|
|
1140
|
-
return appSpaceData.parsedCode.
|
|
1181
|
+
return appSpaceData.parsedCode.replace(new RegExp(createPrefix(item, true), 'g'), prefix);
|
|
1141
1182
|
}
|
|
1142
1183
|
}
|
|
1143
1184
|
}
|
|
@@ -1223,18 +1264,18 @@ function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
|
|
|
1223
1264
|
const linkInfo = sourceCenter.link.getInfo(address);
|
|
1224
1265
|
return linkInfo.code ? linkInfo.code : fetchSource(address, app.name);
|
|
1225
1266
|
});
|
|
1226
|
-
const fiberLinkTasks =
|
|
1267
|
+
const fiberLinkTasks = fiberStyleResult ? [] : null;
|
|
1227
1268
|
promiseStream(fetchLinkPromise, (res) => {
|
|
1228
1269
|
injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
|
|
1229
1270
|
}, (err) => {
|
|
1230
1271
|
logError(err, app.name);
|
|
1231
1272
|
}, () => {
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1273
|
+
/**
|
|
1274
|
+
* 1. If fiberStyleResult exist, fiberLinkTasks must exist
|
|
1275
|
+
* 2. Download link source while processing style
|
|
1276
|
+
* 3. Process style first, and then process link
|
|
1277
|
+
*/
|
|
1278
|
+
if (fiberStyleResult) {
|
|
1238
1279
|
fiberStyleResult.then(() => {
|
|
1239
1280
|
fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
|
|
1240
1281
|
serialExecFiberTasks(fiberLinkTasks);
|
|
@@ -1368,10 +1409,10 @@ class Adapter {
|
|
|
1368
1409
|
'webpackHotUpdate',
|
|
1369
1410
|
'Vue',
|
|
1370
1411
|
];
|
|
1371
|
-
this.
|
|
1412
|
+
this.injectReactHMRProperty();
|
|
1372
1413
|
}
|
|
1373
1414
|
// adapter for react
|
|
1374
|
-
|
|
1415
|
+
injectReactHMRProperty() {
|
|
1375
1416
|
if ((process.env.NODE_ENV !== 'production')) {
|
|
1376
1417
|
// react child in non-react env
|
|
1377
1418
|
this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
|
|
@@ -1410,13 +1451,15 @@ function fixReactHMRConflict(app) {
|
|
|
1410
1451
|
/**
|
|
1411
1452
|
* reDefine parentNode of html
|
|
1412
1453
|
* Scenes:
|
|
1413
|
-
* 1. element-ui
|
|
1414
|
-
*
|
|
1454
|
+
* 1. element-ui@2/lib/utils/popper.js
|
|
1455
|
+
* var parent = element.parentNode;
|
|
1456
|
+
* // root is child app window
|
|
1457
|
+
* if (parent === root.document) ...
|
|
1415
1458
|
*/
|
|
1416
|
-
function throttleDeferForParentNode(
|
|
1459
|
+
function throttleDeferForParentNode(microDocument) {
|
|
1417
1460
|
const html = globalEnv.rawDocument.firstElementChild;
|
|
1418
|
-
if (html
|
|
1419
|
-
setParentNode(html,
|
|
1461
|
+
if ((html === null || html === void 0 ? void 0 : html.parentNode) === globalEnv.rawDocument) {
|
|
1462
|
+
setParentNode(html, microDocument);
|
|
1420
1463
|
defer(() => {
|
|
1421
1464
|
setParentNode(html, globalEnv.rawDocument);
|
|
1422
1465
|
});
|
|
@@ -1436,6 +1479,64 @@ function setParentNode(target, value) {
|
|
|
1436
1479
|
});
|
|
1437
1480
|
}
|
|
1438
1481
|
}
|
|
1482
|
+
// this events should be sent to the specified app
|
|
1483
|
+
const formatEventList = ['unmount', 'appstate-change'];
|
|
1484
|
+
/**
|
|
1485
|
+
* Format event name
|
|
1486
|
+
* @param eventName event name
|
|
1487
|
+
* @param appName app name
|
|
1488
|
+
*/
|
|
1489
|
+
function formatEventName(eventName, appName) {
|
|
1490
|
+
var _a;
|
|
1491
|
+
if (!isIframeSandbox(appName) && (formatEventList.includes(eventName) ||
|
|
1492
|
+
((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter)))) {
|
|
1493
|
+
return `${eventName}-${appName}`;
|
|
1494
|
+
}
|
|
1495
|
+
return eventName;
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* update dom tree of target dom
|
|
1499
|
+
* @param container target dom
|
|
1500
|
+
* @param appName app name
|
|
1501
|
+
*/
|
|
1502
|
+
function patchElementTree(container, appName) {
|
|
1503
|
+
const children = Array.from(container.children);
|
|
1504
|
+
children.length && children.forEach((child) => {
|
|
1505
|
+
patchElementTree(child, appName);
|
|
1506
|
+
});
|
|
1507
|
+
for (const child of children) {
|
|
1508
|
+
updateElementInfo(child, appName);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* rewrite baseURI, ownerDocument, __MICRO_APP_NAME__ of target node
|
|
1513
|
+
* @param node target node
|
|
1514
|
+
* @param appName app name
|
|
1515
|
+
* @returns target node
|
|
1516
|
+
*/
|
|
1517
|
+
function updateElementInfo(node, appName) {
|
|
1518
|
+
var _a, _b;
|
|
1519
|
+
const proxyWindow = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow;
|
|
1520
|
+
if (proxyWindow && isNode(node) && !node.__MICRO_APP_NAME__) {
|
|
1521
|
+
// TODO: 测试baseURI和ownerDocument在with沙箱中是否正确
|
|
1522
|
+
rawDefineProperties(node, {
|
|
1523
|
+
baseURI: {
|
|
1524
|
+
configurable: true,
|
|
1525
|
+
get: () => proxyWindow.location.href,
|
|
1526
|
+
},
|
|
1527
|
+
ownerDocument: {
|
|
1528
|
+
configurable: true,
|
|
1529
|
+
get: () => proxyWindow.document,
|
|
1530
|
+
},
|
|
1531
|
+
__MICRO_APP_NAME__: {
|
|
1532
|
+
configurable: true,
|
|
1533
|
+
writable: true,
|
|
1534
|
+
value: appName,
|
|
1535
|
+
},
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
return node;
|
|
1539
|
+
}
|
|
1439
1540
|
|
|
1440
1541
|
// Record element and map element
|
|
1441
1542
|
const dynamicElementInMicroAppMap = new WeakMap();
|
|
@@ -1446,7 +1547,7 @@ const dynamicElementInMicroAppMap = new WeakMap();
|
|
|
1446
1547
|
* @param app app
|
|
1447
1548
|
*/
|
|
1448
1549
|
function handleNewNode(parent, child, app) {
|
|
1449
|
-
if (child
|
|
1550
|
+
if (isStyleElement(child)) {
|
|
1450
1551
|
if (child.hasAttribute('exclude')) {
|
|
1451
1552
|
const replaceComment = document.createComment('style element with exclude attribute ignored by micro-app');
|
|
1452
1553
|
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
@@ -1457,7 +1558,7 @@ function handleNewNode(parent, child, app) {
|
|
|
1457
1558
|
}
|
|
1458
1559
|
return child;
|
|
1459
1560
|
}
|
|
1460
|
-
else if (child
|
|
1561
|
+
else if (isLinkElement(child)) {
|
|
1461
1562
|
if (child.hasAttribute('exclude') || checkExcludeUrl(child.getAttribute('href'), app.name)) {
|
|
1462
1563
|
const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
|
|
1463
1564
|
dynamicElementInMicroAppMap.set(child, linkReplaceComment);
|
|
@@ -1482,7 +1583,7 @@ function handleNewNode(parent, child, app) {
|
|
|
1482
1583
|
}
|
|
1483
1584
|
return child;
|
|
1484
1585
|
}
|
|
1485
|
-
else if (child
|
|
1586
|
+
else if (isScriptElement(child)) {
|
|
1486
1587
|
if (child.src &&
|
|
1487
1588
|
isFunction(microApp.options.excludeAssetFilter) &&
|
|
1488
1589
|
microApp.options.excludeAssetFilter(child.src)) {
|
|
@@ -1512,17 +1613,20 @@ function handleNewNode(parent, child, app) {
|
|
|
1512
1613
|
* @param passiveChild second param of insertBefore and replaceChild
|
|
1513
1614
|
*/
|
|
1514
1615
|
function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
|
|
1515
|
-
const hijackParent = getHijackParent(parent, app);
|
|
1616
|
+
const hijackParent = getHijackParent(parent, targetChild, app);
|
|
1516
1617
|
/**
|
|
1517
1618
|
* If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
|
|
1518
1619
|
* E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
|
|
1519
1620
|
*/
|
|
1520
1621
|
if (hijackParent) {
|
|
1521
1622
|
/**
|
|
1623
|
+
* Adapter for
|
|
1522
1624
|
* WARNING:
|
|
1523
1625
|
* Verifying that the parentNode of the targetChild points to document.body will cause other problems ?
|
|
1524
1626
|
*/
|
|
1525
|
-
if (
|
|
1627
|
+
if (!isIframeSandbox(app.name) &&
|
|
1628
|
+
hijackParent.tagName === 'MICRO-APP-BODY' &&
|
|
1629
|
+
rawMethod !== globalEnv.rawRemoveChild) {
|
|
1526
1630
|
const descriptor = Object.getOwnPropertyDescriptor(targetChild, 'parentNode');
|
|
1527
1631
|
if (!descriptor || descriptor.configurable) {
|
|
1528
1632
|
rawDefineProperty(targetChild, 'parentNode', {
|
|
@@ -1552,7 +1656,7 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
|
|
|
1552
1656
|
return targetChild;
|
|
1553
1657
|
}
|
|
1554
1658
|
if ((process.env.NODE_ENV !== 'production') &&
|
|
1555
|
-
targetChild
|
|
1659
|
+
isIFrameElement(targetChild) &&
|
|
1556
1660
|
rawMethod === globalEnv.rawAppendChild) {
|
|
1557
1661
|
fixReactHMRConflict(app);
|
|
1558
1662
|
}
|
|
@@ -1561,13 +1665,20 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
|
|
|
1561
1665
|
return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
|
|
1562
1666
|
}
|
|
1563
1667
|
// head/body map to micro-app-head/micro-app-body
|
|
1564
|
-
function getHijackParent(
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1668
|
+
function getHijackParent(parent, targetChild, app) {
|
|
1669
|
+
if (app) {
|
|
1670
|
+
if (parent === document.head) {
|
|
1671
|
+
if (app.iframe && isScriptElement(targetChild)) {
|
|
1672
|
+
return app.sandBox.microHead;
|
|
1673
|
+
}
|
|
1674
|
+
return app.querySelector('micro-app-head');
|
|
1675
|
+
}
|
|
1676
|
+
if (parent === document.body || parent === document.body.parentNode) {
|
|
1677
|
+
if (app.iframe && isScriptElement(targetChild)) {
|
|
1678
|
+
return app.sandBox.microBody;
|
|
1679
|
+
}
|
|
1680
|
+
return app.querySelector('micro-app-body');
|
|
1681
|
+
}
|
|
1571
1682
|
}
|
|
1572
1683
|
return null;
|
|
1573
1684
|
}
|
|
@@ -1595,8 +1706,9 @@ function getMappingNode(node) {
|
|
|
1595
1706
|
function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
|
|
1596
1707
|
const currentAppName = getCurrentAppName();
|
|
1597
1708
|
if (isNode(newChild) &&
|
|
1709
|
+
!newChild.__PURE_ELEMENT__ &&
|
|
1598
1710
|
(newChild.__MICRO_APP_NAME__ ||
|
|
1599
|
-
|
|
1711
|
+
currentAppName)) {
|
|
1600
1712
|
newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
|
|
1601
1713
|
const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
|
|
1602
1714
|
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
@@ -1624,10 +1736,10 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
|
|
|
1624
1736
|
const app = appInstanceMap.get(currentAppName);
|
|
1625
1737
|
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
1626
1738
|
if (parent === document.head) {
|
|
1627
|
-
return rawMethod.call(app.
|
|
1739
|
+
return rawMethod.call(app.querySelector('micro-app-head'), newChild);
|
|
1628
1740
|
}
|
|
1629
1741
|
else if (parent === document.body) {
|
|
1630
|
-
return rawMethod.call(app.
|
|
1742
|
+
return rawMethod.call(app.querySelector('micro-app-body'), newChild);
|
|
1631
1743
|
}
|
|
1632
1744
|
}
|
|
1633
1745
|
}
|
|
@@ -1638,7 +1750,7 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
|
|
|
1638
1750
|
/**
|
|
1639
1751
|
* Rewrite element prototype method
|
|
1640
1752
|
*/
|
|
1641
|
-
function
|
|
1753
|
+
function patchElementAndDocument() {
|
|
1642
1754
|
patchDocument();
|
|
1643
1755
|
// prototype methods of add element👇
|
|
1644
1756
|
Element.prototype.appendChild = function appendChild(newChild) {
|
|
@@ -1687,6 +1799,71 @@ function patchElementPrototypeMethods() {
|
|
|
1687
1799
|
this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
|
|
1688
1800
|
return clonedNode;
|
|
1689
1801
|
};
|
|
1802
|
+
function getQueryTarget(node) {
|
|
1803
|
+
const currentAppName = getCurrentAppName();
|
|
1804
|
+
if ((node === document.body || node === document.head) && currentAppName) {
|
|
1805
|
+
const app = appInstanceMap.get(currentAppName);
|
|
1806
|
+
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
1807
|
+
if (node === document.body) {
|
|
1808
|
+
return app.querySelector('micro-app-body');
|
|
1809
|
+
}
|
|
1810
|
+
else if (node === document.head) {
|
|
1811
|
+
return app.querySelector('micro-app-head');
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
return null;
|
|
1816
|
+
}
|
|
1817
|
+
Element.prototype.querySelector = function querySelector(selectors) {
|
|
1818
|
+
var _a;
|
|
1819
|
+
const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
|
|
1820
|
+
return globalEnv.rawElementQuerySelector.call(target, selectors);
|
|
1821
|
+
};
|
|
1822
|
+
Element.prototype.querySelectorAll = function querySelectorAll(selectors) {
|
|
1823
|
+
var _a;
|
|
1824
|
+
const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
|
|
1825
|
+
return globalEnv.rawElementQuerySelectorAll.call(target, selectors);
|
|
1826
|
+
};
|
|
1827
|
+
rawDefineProperty(Element.prototype, 'innerHTML', {
|
|
1828
|
+
configurable: true,
|
|
1829
|
+
enumerable: true,
|
|
1830
|
+
get() {
|
|
1831
|
+
return globalEnv.rawInnerHTMLDesc.get.call(this);
|
|
1832
|
+
},
|
|
1833
|
+
set(code) {
|
|
1834
|
+
globalEnv.rawInnerHTMLDesc.set.call(this, code);
|
|
1835
|
+
const currentAppName = getCurrentAppName();
|
|
1836
|
+
Array.from(this.children).forEach((child) => {
|
|
1837
|
+
if (isElement(child) && currentAppName) {
|
|
1838
|
+
child.__MICRO_APP_NAME__ = currentAppName;
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1843
|
+
// Abandon this way at 2023.2.28 before v1.0.0-beta.0, it will cause vue2 throw error when render again
|
|
1844
|
+
// rawDefineProperty(Node.prototype, 'parentNode', {
|
|
1845
|
+
// configurable: true,
|
|
1846
|
+
// enumerable: true,
|
|
1847
|
+
// get () {
|
|
1848
|
+
// const result = globalEnv.rawParentNodeDesc.get.call(this)
|
|
1849
|
+
// /**
|
|
1850
|
+
// * If parentNode is <micro-app-body>, return rawDocument.body
|
|
1851
|
+
// * Scenes:
|
|
1852
|
+
// * 1. element-ui@2/lib/utils/vue-popper.js
|
|
1853
|
+
// * if (this.popperElm.parentNode === document.body) ...
|
|
1854
|
+
// * WARNING:
|
|
1855
|
+
// * Will it cause other problems ?
|
|
1856
|
+
// * e.g. target.parentNode.remove(target)
|
|
1857
|
+
// * BUG:
|
|
1858
|
+
// * 1. vue2 umdMode, throw error when render again (<div id='app'></div> will be deleted when render again )
|
|
1859
|
+
// */
|
|
1860
|
+
// if (result?.tagName === 'MICRO-APP-BODY' && appInstanceMap.get(this.__MICRO_APP_NAME__)?.container) {
|
|
1861
|
+
// return globalEnv.rawDocument.body
|
|
1862
|
+
// }
|
|
1863
|
+
// return result
|
|
1864
|
+
// },
|
|
1865
|
+
// set: undefined,
|
|
1866
|
+
// })
|
|
1690
1867
|
}
|
|
1691
1868
|
/**
|
|
1692
1869
|
* Mark the newly created element in the micro application
|
|
@@ -1720,7 +1897,7 @@ function patchDocument() {
|
|
|
1720
1897
|
};
|
|
1721
1898
|
// query element👇
|
|
1722
1899
|
function querySelector(selectors) {
|
|
1723
|
-
var _a, _b, _c
|
|
1900
|
+
var _a, _b, _c;
|
|
1724
1901
|
const _this = getBindTarget(this);
|
|
1725
1902
|
const currentAppName = getCurrentAppName();
|
|
1726
1903
|
if (!currentAppName ||
|
|
@@ -1731,10 +1908,10 @@ function patchDocument() {
|
|
|
1731
1908
|
rawDocument !== _this) {
|
|
1732
1909
|
return globalEnv.rawQuerySelector.call(_this, selectors);
|
|
1733
1910
|
}
|
|
1734
|
-
return (
|
|
1911
|
+
return (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
|
|
1735
1912
|
}
|
|
1736
1913
|
function querySelectorAll(selectors) {
|
|
1737
|
-
var _a, _b, _c
|
|
1914
|
+
var _a, _b, _c;
|
|
1738
1915
|
const _this = getBindTarget(this);
|
|
1739
1916
|
const currentAppName = getCurrentAppName();
|
|
1740
1917
|
if (!currentAppName ||
|
|
@@ -1744,7 +1921,7 @@ function patchDocument() {
|
|
|
1744
1921
|
rawDocument !== _this) {
|
|
1745
1922
|
return globalEnv.rawQuerySelectorAll.call(_this, selectors);
|
|
1746
1923
|
}
|
|
1747
|
-
return (
|
|
1924
|
+
return (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.querySelectorAll(selectors)) !== null && _c !== void 0 ? _c : [];
|
|
1748
1925
|
}
|
|
1749
1926
|
rawRootDocument.prototype.querySelector = querySelector;
|
|
1750
1927
|
rawRootDocument.prototype.querySelectorAll = querySelectorAll;
|
|
@@ -1832,7 +2009,7 @@ function patchSetAttribute() {
|
|
|
1832
2009
|
const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
|
|
1833
2010
|
if (appName &&
|
|
1834
2011
|
appInstanceMap.has(appName) &&
|
|
1835
|
-
(((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
|
|
2012
|
+
(((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
|
|
1836
2013
|
(key === 'href' && /^link$/i.test(this.tagName)))) {
|
|
1837
2014
|
const app = appInstanceMap.get(appName);
|
|
1838
2015
|
value = CompletionPath(value, app.url);
|
|
@@ -1854,7 +2031,7 @@ function releasePatchDocument() {
|
|
|
1854
2031
|
rawRootDocument.prototype.getElementsByName = globalEnv.rawGetElementsByName;
|
|
1855
2032
|
}
|
|
1856
2033
|
// release patch
|
|
1857
|
-
function
|
|
2034
|
+
function releasePatchElementAndDocument() {
|
|
1858
2035
|
removeDomScope();
|
|
1859
2036
|
releasePatchDocument();
|
|
1860
2037
|
Element.prototype.appendChild = globalEnv.rawAppendChild;
|
|
@@ -1864,6 +2041,9 @@ function releasePatches() {
|
|
|
1864
2041
|
Element.prototype.append = globalEnv.rawAppend;
|
|
1865
2042
|
Element.prototype.prepend = globalEnv.rawPrepend;
|
|
1866
2043
|
Element.prototype.cloneNode = globalEnv.rawCloneNode;
|
|
2044
|
+
Element.prototype.querySelector = globalEnv.rawElementQuerySelector;
|
|
2045
|
+
Element.prototype.querySelectorAll = globalEnv.rawElementQuerySelectorAll;
|
|
2046
|
+
rawDefineProperty(Element.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
|
|
1867
2047
|
}
|
|
1868
2048
|
// exec when last child unmount
|
|
1869
2049
|
function releasePatchSetAttribute() {
|
|
@@ -1882,16 +2062,21 @@ function rejectMicroAppStyle() {
|
|
|
1882
2062
|
}
|
|
1883
2063
|
}
|
|
1884
2064
|
|
|
1885
|
-
const globalEnv = {
|
|
2065
|
+
const globalEnv = {
|
|
2066
|
+
// mark current application as base application
|
|
2067
|
+
__MICRO_APP_BASE_APPLICATION__: true,
|
|
2068
|
+
// active sandbox count
|
|
2069
|
+
activeSandbox: 0,
|
|
2070
|
+
};
|
|
1886
2071
|
/**
|
|
1887
2072
|
* Note loop nesting
|
|
1888
2073
|
* Only prototype or unique values can be put here
|
|
1889
2074
|
*/
|
|
1890
2075
|
function initGlobalEnv() {
|
|
1891
2076
|
if (isBrowser) {
|
|
1892
|
-
const rawWindow = Function('return window')();
|
|
1893
|
-
const rawDocument = Function('return document')();
|
|
1894
|
-
const rawRootDocument = Function('return Document')();
|
|
2077
|
+
const rawWindow = window.rawWindow || Function('return window')();
|
|
2078
|
+
const rawDocument = window.rawDocument || Function('return document')();
|
|
2079
|
+
const rawRootDocument = rawWindow.Document || Function('return Document')();
|
|
1895
2080
|
const supportModuleScript = isSupportModuleScript();
|
|
1896
2081
|
/**
|
|
1897
2082
|
* save patch raw methods
|
|
@@ -1905,9 +2090,14 @@ function initGlobalEnv() {
|
|
|
1905
2090
|
const rawAppend = Element.prototype.append;
|
|
1906
2091
|
const rawPrepend = Element.prototype.prepend;
|
|
1907
2092
|
const rawCloneNode = Element.prototype.cloneNode;
|
|
2093
|
+
const rawElementQuerySelector = Element.prototype.querySelector;
|
|
2094
|
+
const rawElementQuerySelectorAll = Element.prototype.querySelectorAll;
|
|
2095
|
+
const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
|
|
2096
|
+
const rawParentNodeDesc = Object.getOwnPropertyDescriptor(Node.prototype, 'parentNode');
|
|
1908
2097
|
const rawCreateElement = rawRootDocument.prototype.createElement;
|
|
1909
2098
|
const rawCreateElementNS = rawRootDocument.prototype.createElementNS;
|
|
1910
2099
|
const rawCreateDocumentFragment = rawRootDocument.prototype.createDocumentFragment;
|
|
2100
|
+
const rawCreateTextNode = rawRootDocument.prototype.createTextNode;
|
|
1911
2101
|
const rawQuerySelector = rawRootDocument.prototype.querySelector;
|
|
1912
2102
|
const rawQuerySelectorAll = rawRootDocument.prototype.querySelectorAll;
|
|
1913
2103
|
const rawGetElementById = rawRootDocument.prototype.getElementById;
|
|
@@ -1925,18 +2115,19 @@ function initGlobalEnv() {
|
|
|
1925
2115
|
* save effect raw methods
|
|
1926
2116
|
* pay attention to this binding, especially setInterval, setTimeout, clearInterval, clearTimeout
|
|
1927
2117
|
*/
|
|
1928
|
-
const rawWindowAddEventListener = rawWindow.addEventListener;
|
|
1929
|
-
const rawWindowRemoveEventListener = rawWindow.removeEventListener;
|
|
1930
2118
|
const rawSetInterval = rawWindow.setInterval;
|
|
1931
2119
|
const rawSetTimeout = rawWindow.setTimeout;
|
|
1932
2120
|
const rawClearInterval = rawWindow.clearInterval;
|
|
1933
2121
|
const rawClearTimeout = rawWindow.clearTimeout;
|
|
1934
2122
|
const rawPushState = rawWindow.history.pushState;
|
|
1935
2123
|
const rawReplaceState = rawWindow.history.replaceState;
|
|
2124
|
+
const rawWindowAddEventListener = rawWindow.addEventListener;
|
|
2125
|
+
const rawWindowRemoveEventListener = rawWindow.removeEventListener;
|
|
1936
2126
|
const rawDocumentAddEventListener = rawDocument.addEventListener;
|
|
1937
2127
|
const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
|
|
1938
|
-
//
|
|
1939
|
-
|
|
2128
|
+
// TODO: 统一使用 EventTarget 去掉上面四个
|
|
2129
|
+
const rawAddEventListener = EventTarget.prototype.addEventListener;
|
|
2130
|
+
const rawRemoveEventListener = EventTarget.prototype.removeEventListener;
|
|
1940
2131
|
assign(globalEnv, {
|
|
1941
2132
|
// common global vars
|
|
1942
2133
|
rawWindow,
|
|
@@ -1952,9 +2143,14 @@ function initGlobalEnv() {
|
|
|
1952
2143
|
rawAppend,
|
|
1953
2144
|
rawPrepend,
|
|
1954
2145
|
rawCloneNode,
|
|
2146
|
+
rawElementQuerySelector,
|
|
2147
|
+
rawElementQuerySelectorAll,
|
|
2148
|
+
rawInnerHTMLDesc,
|
|
2149
|
+
rawParentNodeDesc,
|
|
1955
2150
|
rawCreateElement,
|
|
1956
2151
|
rawCreateElementNS,
|
|
1957
2152
|
rawCreateDocumentFragment,
|
|
2153
|
+
rawCreateTextNode,
|
|
1958
2154
|
rawQuerySelector,
|
|
1959
2155
|
rawQuerySelectorAll,
|
|
1960
2156
|
rawGetElementById,
|
|
@@ -1973,6 +2169,8 @@ function initGlobalEnv() {
|
|
|
1973
2169
|
rawDocumentRemoveEventListener,
|
|
1974
2170
|
rawPushState,
|
|
1975
2171
|
rawReplaceState,
|
|
2172
|
+
rawAddEventListener,
|
|
2173
|
+
rawRemoveEventListener,
|
|
1976
2174
|
});
|
|
1977
2175
|
// global effect
|
|
1978
2176
|
rejectMicroAppStyle();
|
|
@@ -1982,7 +2180,7 @@ function initGlobalEnv() {
|
|
|
1982
2180
|
const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
|
|
1983
2181
|
// whether use type='module' script
|
|
1984
2182
|
function isTypeModule(app, scriptInfo) {
|
|
1985
|
-
return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.
|
|
2183
|
+
return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.iframe);
|
|
1986
2184
|
}
|
|
1987
2185
|
// special script element
|
|
1988
2186
|
function isSpecialScript(app, scriptInfo) {
|
|
@@ -2001,11 +2199,17 @@ function isInlineMode(app, scriptInfo) {
|
|
|
2001
2199
|
return (app.inline ||
|
|
2002
2200
|
scriptInfo.appSpace[app.name].inline ||
|
|
2003
2201
|
isTypeModule(app, scriptInfo) ||
|
|
2004
|
-
isSpecialScript(app, scriptInfo)
|
|
2202
|
+
isSpecialScript(app, scriptInfo) ||
|
|
2203
|
+
app.iframe);
|
|
2204
|
+
}
|
|
2205
|
+
// TODO: iframe重新插入window前后不一致,通过iframe Function创建的函数无法复用
|
|
2206
|
+
function getEffectWindow(app) {
|
|
2207
|
+
return app.iframe ? app.sandBox.microAppWindow : globalEnv.rawWindow;
|
|
2005
2208
|
}
|
|
2006
2209
|
// Convert string code to function
|
|
2007
|
-
function code2Function(code) {
|
|
2008
|
-
|
|
2210
|
+
function code2Function(app, code) {
|
|
2211
|
+
const targetWindow = getEffectWindow(app);
|
|
2212
|
+
return new targetWindow.Function(code);
|
|
2009
2213
|
}
|
|
2010
2214
|
/**
|
|
2011
2215
|
* If the appSpace of the current js address has other app, try to reuse parsedFunction of other app
|
|
@@ -2013,10 +2217,10 @@ function code2Function(code) {
|
|
|
2013
2217
|
* @param scriptInfo scriptInfo of current address
|
|
2014
2218
|
* @param currentCode pure code of current address
|
|
2015
2219
|
*/
|
|
2016
|
-
function getExistParseResult(
|
|
2220
|
+
function getExistParseResult(app, scriptInfo, currentCode) {
|
|
2017
2221
|
const appSpace = scriptInfo.appSpace;
|
|
2018
2222
|
for (const item in appSpace) {
|
|
2019
|
-
if (item !==
|
|
2223
|
+
if (item !== app.name) {
|
|
2020
2224
|
const appSpaceData = appSpace[item];
|
|
2021
2225
|
if (appSpaceData.parsedCode === currentCode && appSpaceData.parsedFunction) {
|
|
2022
2226
|
return appSpaceData.parsedFunction;
|
|
@@ -2029,7 +2233,7 @@ function getExistParseResult(appName, scriptInfo, currentCode) {
|
|
|
2029
2233
|
* @returns parsedFunction
|
|
2030
2234
|
*/
|
|
2031
2235
|
function getParsedFunction(app, scriptInfo, parsedCode) {
|
|
2032
|
-
return getExistParseResult(app
|
|
2236
|
+
return getExistParseResult(app, scriptInfo, parsedCode) || code2Function(app, parsedCode);
|
|
2033
2237
|
}
|
|
2034
2238
|
// Prevent randomly created strings from repeating
|
|
2035
2239
|
function getUniqueNonceSrc() {
|
|
@@ -2053,6 +2257,9 @@ function setConvertScriptAttr(convertScript, attrs) {
|
|
|
2053
2257
|
function isWrapInSandBox(app, scriptInfo) {
|
|
2054
2258
|
return app.useSandbox && !isTypeModule(app, scriptInfo);
|
|
2055
2259
|
}
|
|
2260
|
+
function getSandboxType(app, scriptInfo) {
|
|
2261
|
+
return isWrapInSandBox(app, scriptInfo) ? app.iframe ? 'iframe' : 'with' : 'disable';
|
|
2262
|
+
}
|
|
2056
2263
|
/**
|
|
2057
2264
|
* Extract script elements
|
|
2058
2265
|
* @param script script element
|
|
@@ -2061,6 +2268,7 @@ function isWrapInSandBox(app, scriptInfo) {
|
|
|
2061
2268
|
* @param isDynamic dynamic insert
|
|
2062
2269
|
*/
|
|
2063
2270
|
function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
2271
|
+
var _a;
|
|
2064
2272
|
let replaceComment = null;
|
|
2065
2273
|
let src = script.getAttribute('src');
|
|
2066
2274
|
if (src)
|
|
@@ -2072,6 +2280,10 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
|
2072
2280
|
!scriptTypes.includes(script.type)) ||
|
|
2073
2281
|
script.hasAttribute('ignore') ||
|
|
2074
2282
|
checkIgnoreUrl(src, app.name)) {
|
|
2283
|
+
// 配置为忽略的脚本,清空 rawDocument.currentScript,避免被忽略的脚本内获取 currentScript 出错
|
|
2284
|
+
if ((_a = globalEnv.rawDocument) === null || _a === void 0 ? void 0 : _a.currentScript) {
|
|
2285
|
+
delete globalEnv.rawDocument.currentScript;
|
|
2286
|
+
}
|
|
2075
2287
|
return null;
|
|
2076
2288
|
}
|
|
2077
2289
|
else if ((globalEnv.supportModuleScript && script.noModule) ||
|
|
@@ -2267,13 +2479,13 @@ function fetchScriptSuccess(address, scriptInfo, code, app) {
|
|
|
2267
2479
|
*/
|
|
2268
2480
|
if (!appSpaceData.parsedCode) {
|
|
2269
2481
|
appSpaceData.parsedCode = bindScope(address, app, code, scriptInfo);
|
|
2270
|
-
appSpaceData.
|
|
2482
|
+
appSpaceData.sandboxType = getSandboxType(app, scriptInfo);
|
|
2271
2483
|
if (!isInlineMode(app, scriptInfo)) {
|
|
2272
2484
|
try {
|
|
2273
2485
|
appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
|
|
2274
2486
|
}
|
|
2275
2487
|
catch (err) {
|
|
2276
|
-
|
|
2488
|
+
logError('Something went wrong while handling preloaded resources', app.name, '\n', err);
|
|
2277
2489
|
}
|
|
2278
2490
|
}
|
|
2279
2491
|
}
|
|
@@ -2294,7 +2506,8 @@ function execScripts(app, initHook) {
|
|
|
2294
2506
|
const appSpaceData = scriptInfo.appSpace[app.name];
|
|
2295
2507
|
// Notice the second render
|
|
2296
2508
|
if (appSpaceData.defer || appSpaceData.async) {
|
|
2297
|
-
|
|
2509
|
+
// TODO: defer和module彻底分开,不要混在一起
|
|
2510
|
+
if (scriptInfo.isExternal && !scriptInfo.code && !(app.iframe && appSpaceData.module)) {
|
|
2298
2511
|
deferScriptPromise.push(fetchSource(address, app.name));
|
|
2299
2512
|
}
|
|
2300
2513
|
else {
|
|
@@ -2319,7 +2532,7 @@ function execScripts(app, initHook) {
|
|
|
2319
2532
|
logError(err, app.name);
|
|
2320
2533
|
}, () => {
|
|
2321
2534
|
deferScriptInfo.forEach(([address, scriptInfo]) => {
|
|
2322
|
-
if (scriptInfo.code) {
|
|
2535
|
+
if (isString(scriptInfo.code)) {
|
|
2323
2536
|
injectFiberTask(fiberScriptTasks, () => {
|
|
2324
2537
|
runScript(address, app, scriptInfo, initHook);
|
|
2325
2538
|
!isTypeModule(app, scriptInfo) && initHook(false);
|
|
@@ -2363,20 +2576,19 @@ function execScripts(app, initHook) {
|
|
|
2363
2576
|
* @param callback callback of module script
|
|
2364
2577
|
*/
|
|
2365
2578
|
function runScript(address, app, scriptInfo, callback, replaceElement) {
|
|
2366
|
-
var _a;
|
|
2367
2579
|
try {
|
|
2368
2580
|
actionsBeforeRunScript(app);
|
|
2369
2581
|
const appSpaceData = scriptInfo.appSpace[app.name];
|
|
2370
|
-
const
|
|
2582
|
+
const sandboxType = getSandboxType(app, scriptInfo);
|
|
2371
2583
|
/**
|
|
2372
2584
|
* NOTE:
|
|
2373
2585
|
* 1. plugins and wrapCode will only be executed once
|
|
2374
2586
|
* 2. if parsedCode not exist, parsedFunction is not exist
|
|
2375
2587
|
* 3. if parsedCode exist, parsedFunction does not necessarily exist
|
|
2376
2588
|
*/
|
|
2377
|
-
if (!appSpaceData.parsedCode || appSpaceData.
|
|
2589
|
+
if (!appSpaceData.parsedCode || appSpaceData.sandboxType !== sandboxType) {
|
|
2378
2590
|
appSpaceData.parsedCode = bindScope(address, app, scriptInfo.code, scriptInfo);
|
|
2379
|
-
appSpaceData.
|
|
2591
|
+
appSpaceData.sandboxType = sandboxType;
|
|
2380
2592
|
appSpaceData.parsedFunction = null;
|
|
2381
2593
|
}
|
|
2382
2594
|
if (isInlineMode(app, scriptInfo)) {
|
|
@@ -2384,7 +2596,8 @@ function runScript(address, app, scriptInfo, callback, replaceElement) {
|
|
|
2384
2596
|
runCode2InlineScript(address, appSpaceData.parsedCode, isTypeModule(app, scriptInfo), scriptElement, appSpaceData.attrs, callback);
|
|
2385
2597
|
if (!replaceElement) {
|
|
2386
2598
|
// TEST IGNORE
|
|
2387
|
-
|
|
2599
|
+
const parent = app.iframe ? app.sandBox.microBody : app.querySelector('micro-app-body');
|
|
2600
|
+
parent === null || parent === void 0 ? void 0 : parent.appendChild(scriptElement);
|
|
2388
2601
|
}
|
|
2389
2602
|
}
|
|
2390
2603
|
else {
|
|
@@ -2416,7 +2629,7 @@ function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
|
|
|
2416
2629
|
runScript(address, app, scriptInfo, dispatchScriptOnLoadEvent, replaceElement);
|
|
2417
2630
|
!isTypeModule(app, scriptInfo) && dispatchScriptOnLoadEvent();
|
|
2418
2631
|
};
|
|
2419
|
-
if (scriptInfo.code) {
|
|
2632
|
+
if (scriptInfo.code || (app.iframe && scriptInfo.appSpace[app.name].module)) {
|
|
2420
2633
|
defer(runDynamicScript);
|
|
2421
2634
|
}
|
|
2422
2635
|
else {
|
|
@@ -2463,6 +2676,9 @@ function runCode2InlineScript(address, code, module, scriptElement, attrs, callb
|
|
|
2463
2676
|
scriptElement.setAttribute('type', 'module');
|
|
2464
2677
|
if (callback) {
|
|
2465
2678
|
callback.moduleCount && callback.moduleCount--;
|
|
2679
|
+
/**
|
|
2680
|
+
* module script will execute onload method only after it insert to document/iframe
|
|
2681
|
+
*/
|
|
2466
2682
|
scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
|
|
2467
2683
|
}
|
|
2468
2684
|
}
|
|
@@ -2477,7 +2693,7 @@ function runParsedFunction(app, scriptInfo) {
|
|
|
2477
2693
|
if (!appSpaceData.parsedFunction) {
|
|
2478
2694
|
appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
|
|
2479
2695
|
}
|
|
2480
|
-
appSpaceData.parsedFunction.call(
|
|
2696
|
+
appSpaceData.parsedFunction.call(getEffectWindow(app));
|
|
2481
2697
|
}
|
|
2482
2698
|
/**
|
|
2483
2699
|
* bind js scope
|
|
@@ -2486,12 +2702,12 @@ function runParsedFunction(app, scriptInfo) {
|
|
|
2486
2702
|
* @param scriptInfo source script info
|
|
2487
2703
|
*/
|
|
2488
2704
|
function bindScope(address, app, code, scriptInfo) {
|
|
2489
|
-
// TODO: cache
|
|
2705
|
+
// TODO: 1、cache 2、esm code is null
|
|
2490
2706
|
if (isPlainObject(microApp.options.plugins)) {
|
|
2491
2707
|
code = usePlugins(address, code, app.name, microApp.options.plugins);
|
|
2492
2708
|
}
|
|
2493
2709
|
if (isWrapInSandBox(app, scriptInfo)) {
|
|
2494
|
-
return `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
|
|
2710
|
+
return app.iframe ? `(function(window,self,global,location){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyLocation);` : `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
|
|
2495
2711
|
}
|
|
2496
2712
|
return code;
|
|
2497
2713
|
}
|
|
@@ -2554,7 +2770,7 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
|
|
|
2554
2770
|
flatChildren(child, app, microAppHead, fiberStyleTasks);
|
|
2555
2771
|
});
|
|
2556
2772
|
for (const dom of children) {
|
|
2557
|
-
if (dom
|
|
2773
|
+
if (isLinkElement(dom)) {
|
|
2558
2774
|
if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
|
|
2559
2775
|
parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
|
|
2560
2776
|
}
|
|
@@ -2565,7 +2781,7 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
|
|
|
2565
2781
|
dom.setAttribute('href', CompletionPath(dom.getAttribute('href'), app.url));
|
|
2566
2782
|
}
|
|
2567
2783
|
}
|
|
2568
|
-
else if (dom
|
|
2784
|
+
else if (isStyleElement(dom)) {
|
|
2569
2785
|
if (dom.hasAttribute('exclude')) {
|
|
2570
2786
|
parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
|
|
2571
2787
|
}
|
|
@@ -2573,15 +2789,23 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
|
|
|
2573
2789
|
injectFiberTask(fiberStyleTasks, () => scopedCSS(dom, app));
|
|
2574
2790
|
}
|
|
2575
2791
|
}
|
|
2576
|
-
else if (dom
|
|
2792
|
+
else if (isScriptElement(dom)) {
|
|
2577
2793
|
extractScriptElement(dom, parent, app);
|
|
2578
2794
|
}
|
|
2579
|
-
else if (dom
|
|
2580
|
-
parent.removeChild(dom);
|
|
2581
|
-
}
|
|
2582
|
-
else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
|
|
2795
|
+
else if (isImageElement(dom) && dom.hasAttribute('src')) {
|
|
2583
2796
|
dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
|
|
2584
2797
|
}
|
|
2798
|
+
/**
|
|
2799
|
+
* Don't remove meta and title, they have some special scenes
|
|
2800
|
+
* e.g.
|
|
2801
|
+
* document.querySelector('meta[name="viewport"]') // for flexible
|
|
2802
|
+
* document.querySelector('meta[name="baseurl"]').baseurl // for api request
|
|
2803
|
+
*
|
|
2804
|
+
* Title point to main app title, child app title used to be compatible with some special scenes
|
|
2805
|
+
*/
|
|
2806
|
+
// else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
|
|
2807
|
+
// parent.removeChild(dom)
|
|
2808
|
+
// }
|
|
2585
2809
|
}
|
|
2586
2810
|
}
|
|
2587
2811
|
/**
|
|
@@ -2591,8 +2815,8 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
|
|
|
2591
2815
|
*/
|
|
2592
2816
|
function extractSourceDom(htmlStr, app) {
|
|
2593
2817
|
const wrapElement = getWrapElement(htmlStr);
|
|
2594
|
-
const microAppHead =
|
|
2595
|
-
const microAppBody =
|
|
2818
|
+
const microAppHead = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-head');
|
|
2819
|
+
const microAppBody = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-body');
|
|
2596
2820
|
if (!microAppHead || !microAppBody) {
|
|
2597
2821
|
const msg = `element ${microAppHead ? 'body' : 'head'} is missing`;
|
|
2598
2822
|
app.onerror(new Error(msg));
|
|
@@ -2793,7 +3017,7 @@ const eventCenter = new EventCenter();
|
|
|
2793
3017
|
* @param appName app.name
|
|
2794
3018
|
* @param fromBaseApp is from base app
|
|
2795
3019
|
*/
|
|
2796
|
-
function
|
|
3020
|
+
function createEventName(appName, fromBaseApp) {
|
|
2797
3021
|
if (!isString(appName) || !appName)
|
|
2798
3022
|
return '';
|
|
2799
3023
|
return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__`;
|
|
@@ -2872,7 +3096,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
2872
3096
|
* @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
|
|
2873
3097
|
*/
|
|
2874
3098
|
addDataListener(appName, cb, autoTrigger) {
|
|
2875
|
-
eventCenter.on(
|
|
3099
|
+
eventCenter.on(createEventName(formatAppName(appName), false), cb, autoTrigger);
|
|
2876
3100
|
}
|
|
2877
3101
|
/**
|
|
2878
3102
|
* remove listener
|
|
@@ -2880,7 +3104,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
2880
3104
|
* @param cb listener
|
|
2881
3105
|
*/
|
|
2882
3106
|
removeDataListener(appName, cb) {
|
|
2883
|
-
isFunction(cb) && eventCenter.off(
|
|
3107
|
+
isFunction(cb) && eventCenter.off(createEventName(formatAppName(appName), false), cb);
|
|
2884
3108
|
}
|
|
2885
3109
|
/**
|
|
2886
3110
|
* get data from micro app or base app
|
|
@@ -2888,7 +3112,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
2888
3112
|
* @param fromBaseApp whether get data from base app, default is false
|
|
2889
3113
|
*/
|
|
2890
3114
|
getData(appName, fromBaseApp = false) {
|
|
2891
|
-
return eventCenter.getData(
|
|
3115
|
+
return eventCenter.getData(createEventName(formatAppName(appName), fromBaseApp));
|
|
2892
3116
|
}
|
|
2893
3117
|
/**
|
|
2894
3118
|
* Dispatch data to the specified micro app
|
|
@@ -2896,7 +3120,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
2896
3120
|
* @param data data
|
|
2897
3121
|
*/
|
|
2898
3122
|
setData(appName, data, nextStep, force) {
|
|
2899
|
-
eventCenter.dispatch(
|
|
3123
|
+
eventCenter.dispatch(createEventName(formatAppName(appName), true), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
|
|
2900
3124
|
}
|
|
2901
3125
|
forceSetData(appName, data, nextStep) {
|
|
2902
3126
|
this.setData(appName, data, nextStep, true);
|
|
@@ -2907,14 +3131,14 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
2907
3131
|
* @param fromBaseApp whether clear data from child app, default is true
|
|
2908
3132
|
*/
|
|
2909
3133
|
clearData(appName, fromBaseApp = true) {
|
|
2910
|
-
eventCenter.clearData(
|
|
3134
|
+
eventCenter.clearData(createEventName(formatAppName(appName), fromBaseApp));
|
|
2911
3135
|
}
|
|
2912
3136
|
/**
|
|
2913
3137
|
* clear all listener for specified micro app
|
|
2914
3138
|
* @param appName app.name
|
|
2915
3139
|
*/
|
|
2916
3140
|
clearDataListener(appName) {
|
|
2917
|
-
eventCenter.off(
|
|
3141
|
+
eventCenter.off(createEventName(formatAppName(appName), false));
|
|
2918
3142
|
}
|
|
2919
3143
|
}
|
|
2920
3144
|
// Event center for sub app
|
|
@@ -2931,20 +3155,20 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
|
|
|
2931
3155
|
*/
|
|
2932
3156
|
addDataListener(cb, autoTrigger) {
|
|
2933
3157
|
cb.__AUTO_TRIGGER__ = autoTrigger;
|
|
2934
|
-
eventCenter.on(
|
|
3158
|
+
eventCenter.on(createEventName(this.appName, true), cb, autoTrigger);
|
|
2935
3159
|
}
|
|
2936
3160
|
/**
|
|
2937
3161
|
* remove listener
|
|
2938
3162
|
* @param cb listener
|
|
2939
3163
|
*/
|
|
2940
3164
|
removeDataListener(cb) {
|
|
2941
|
-
isFunction(cb) && eventCenter.off(
|
|
3165
|
+
isFunction(cb) && eventCenter.off(createEventName(this.appName, true), cb);
|
|
2942
3166
|
}
|
|
2943
3167
|
/**
|
|
2944
3168
|
* get data from base app
|
|
2945
3169
|
*/
|
|
2946
3170
|
getData(fromBaseApp = true) {
|
|
2947
|
-
return eventCenter.getData(
|
|
3171
|
+
return eventCenter.getData(createEventName(this.appName, fromBaseApp));
|
|
2948
3172
|
}
|
|
2949
3173
|
/**
|
|
2950
3174
|
* dispatch data to base app
|
|
@@ -2952,12 +3176,12 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
|
|
|
2952
3176
|
*/
|
|
2953
3177
|
dispatch(data, nextStep, force) {
|
|
2954
3178
|
removeDomScope();
|
|
2955
|
-
eventCenter.dispatch(
|
|
3179
|
+
eventCenter.dispatch(createEventName(this.appName, false), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force, () => {
|
|
2956
3180
|
const app = appInstanceMap.get(this.appName);
|
|
2957
3181
|
if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
|
|
2958
3182
|
const event = new CustomEvent('datachange', {
|
|
2959
3183
|
detail: {
|
|
2960
|
-
data: eventCenter.getData(
|
|
3184
|
+
data: eventCenter.getData(createEventName(this.appName, false))
|
|
2961
3185
|
}
|
|
2962
3186
|
});
|
|
2963
3187
|
getRootContainer(app.container).dispatchEvent(event);
|
|
@@ -2972,33 +3196,35 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
|
|
|
2972
3196
|
* @param fromBaseApp whether clear data from base app, default is false
|
|
2973
3197
|
*/
|
|
2974
3198
|
clearData(fromBaseApp = false) {
|
|
2975
|
-
eventCenter.clearData(
|
|
3199
|
+
eventCenter.clearData(createEventName(this.appName, fromBaseApp));
|
|
2976
3200
|
}
|
|
2977
3201
|
/**
|
|
2978
3202
|
* clear all listeners
|
|
2979
3203
|
*/
|
|
2980
3204
|
clearDataListener() {
|
|
2981
|
-
eventCenter.off(
|
|
3205
|
+
eventCenter.off(createEventName(this.appName, true));
|
|
2982
3206
|
}
|
|
2983
3207
|
}
|
|
2984
3208
|
/**
|
|
2985
3209
|
* Record UMD function before exec umdHookMount
|
|
2986
|
-
*
|
|
3210
|
+
* NOTE: record maybe call twice when unmount prerender, keep-alive app manually with umd mode
|
|
3211
|
+
* @param microAppEventCenter instance of EventCenterForMicroApp
|
|
2987
3212
|
*/
|
|
2988
3213
|
function recordDataCenterSnapshot(microAppEventCenter) {
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
3214
|
+
if (microAppEventCenter && !microAppEventCenter.umdDataListeners) {
|
|
3215
|
+
microAppEventCenter.umdDataListeners = { global: new Set(), normal: new Set() };
|
|
3216
|
+
const globalEventInfo = eventCenter.eventList.get('global');
|
|
3217
|
+
if (globalEventInfo) {
|
|
3218
|
+
for (const cb of globalEventInfo.callbacks) {
|
|
3219
|
+
if (microAppEventCenter.appName === cb.__APP_NAME__) {
|
|
3220
|
+
microAppEventCenter.umdDataListeners.global.add(cb);
|
|
3221
|
+
}
|
|
2996
3222
|
}
|
|
2997
3223
|
}
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3224
|
+
const subAppEventInfo = eventCenter.eventList.get(createEventName(microAppEventCenter.appName, true));
|
|
3225
|
+
if (subAppEventInfo) {
|
|
3226
|
+
microAppEventCenter.umdDataListeners.normal = new Set(subAppEventInfo.callbacks);
|
|
3227
|
+
}
|
|
3002
3228
|
}
|
|
3003
3229
|
}
|
|
3004
3230
|
/**
|
|
@@ -3006,13 +3232,24 @@ function recordDataCenterSnapshot(microAppEventCenter) {
|
|
|
3006
3232
|
* @param microAppEventCenter instance of EventCenterForMicroApp
|
|
3007
3233
|
*/
|
|
3008
3234
|
function rebuildDataCenterSnapshot(microAppEventCenter) {
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3235
|
+
// in withSandbox preRender mode with module script, umdDataListeners maybe undefined
|
|
3236
|
+
if (microAppEventCenter === null || microAppEventCenter === void 0 ? void 0 : microAppEventCenter.umdDataListeners) {
|
|
3237
|
+
for (const cb of microAppEventCenter.umdDataListeners.global) {
|
|
3238
|
+
microAppEventCenter.addGlobalDataListener(cb, cb.__AUTO_TRIGGER__);
|
|
3239
|
+
}
|
|
3240
|
+
for (const cb of microAppEventCenter.umdDataListeners.normal) {
|
|
3241
|
+
microAppEventCenter.addDataListener(cb, cb.__AUTO_TRIGGER__);
|
|
3242
|
+
}
|
|
3243
|
+
resetDataCenterSnapshot(microAppEventCenter);
|
|
3014
3244
|
}
|
|
3015
3245
|
}
|
|
3246
|
+
/**
|
|
3247
|
+
* delete umdDataListeners from microAppEventCenter
|
|
3248
|
+
* @param microAppEventCenter instance of EventCenterForMicroApp
|
|
3249
|
+
*/
|
|
3250
|
+
function resetDataCenterSnapshot(microAppEventCenter) {
|
|
3251
|
+
delete microAppEventCenter.umdDataListeners;
|
|
3252
|
+
}
|
|
3016
3253
|
|
|
3017
3254
|
// 管理 app 的单例
|
|
3018
3255
|
class AppManager {
|
|
@@ -3075,12 +3312,12 @@ function isConstructorFunction(value) {
|
|
|
3075
3312
|
return value.__MICRO_APP_IS_CONSTRUCTOR__ = isConstructor(value);
|
|
3076
3313
|
}
|
|
3077
3314
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
3078
|
-
function
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
const bindRawObjectValue = value.bind(
|
|
3315
|
+
function bindFunctionToRawTarget(value, rawTarget, key = 'WINDOW') {
|
|
3316
|
+
if (isFunction(value) && !isConstructorFunction(value) && !isBoundedFunction(value)) {
|
|
3317
|
+
const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
|
|
3318
|
+
if (value[cacheKey])
|
|
3319
|
+
return value[cacheKey];
|
|
3320
|
+
const bindRawObjectValue = value.bind(rawTarget);
|
|
3084
3321
|
for (const key in value) {
|
|
3085
3322
|
bindRawObjectValue[key] = value[key];
|
|
3086
3323
|
}
|
|
@@ -3097,21 +3334,6 @@ function bindFunctionToRawObject(rawObject, value, key = 'WINDOW') {
|
|
|
3097
3334
|
return value;
|
|
3098
3335
|
}
|
|
3099
3336
|
|
|
3100
|
-
// this events should be sent to the specified app
|
|
3101
|
-
const formatEventList = ['unmount', 'appstate-change'];
|
|
3102
|
-
/**
|
|
3103
|
-
* Format event name
|
|
3104
|
-
* @param eventName event name
|
|
3105
|
-
* @param appName app name
|
|
3106
|
-
*/
|
|
3107
|
-
function formatEventName$1(eventName, appName) {
|
|
3108
|
-
var _a;
|
|
3109
|
-
if (formatEventList.includes(eventName) ||
|
|
3110
|
-
((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter))) {
|
|
3111
|
-
return `${eventName}-${appName}`;
|
|
3112
|
-
}
|
|
3113
|
-
return eventName;
|
|
3114
|
-
}
|
|
3115
3337
|
// document.onclick binding list, the binding function of each application is unique
|
|
3116
3338
|
const documentClickListMap = new Map();
|
|
3117
3339
|
let hasRewriteDocumentOnClick = false;
|
|
@@ -3222,7 +3444,7 @@ function effect(appName, microAppWindow) {
|
|
|
3222
3444
|
const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
|
|
3223
3445
|
// listener may be null, e.g test-passive
|
|
3224
3446
|
microAppWindow.addEventListener = function (type, listener, options) {
|
|
3225
|
-
type = formatEventName
|
|
3447
|
+
type = formatEventName(type, appName);
|
|
3226
3448
|
const listenerList = eventListenerMap.get(type);
|
|
3227
3449
|
if (listenerList) {
|
|
3228
3450
|
listenerList.add(listener);
|
|
@@ -3234,7 +3456,7 @@ function effect(appName, microAppWindow) {
|
|
|
3234
3456
|
rawWindowAddEventListener.call(rawWindow, type, listener, options);
|
|
3235
3457
|
};
|
|
3236
3458
|
microAppWindow.removeEventListener = function (type, listener, options) {
|
|
3237
|
-
type = formatEventName
|
|
3459
|
+
type = formatEventName(type, appName);
|
|
3238
3460
|
const listenerList = eventListenerMap.get(type);
|
|
3239
3461
|
if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
|
|
3240
3462
|
listenerList.delete(listener);
|
|
@@ -3261,39 +3483,32 @@ function effect(appName, microAppWindow) {
|
|
|
3261
3483
|
};
|
|
3262
3484
|
const sstWindowListenerMap = new Map();
|
|
3263
3485
|
const sstDocumentListenerMap = new Map();
|
|
3264
|
-
let sstIntervalIdMap = new Map();
|
|
3265
|
-
let sstTimeoutIdMap = new Map();
|
|
3266
3486
|
let sstOnClickHandler;
|
|
3267
|
-
|
|
3487
|
+
// reset snapshot data
|
|
3488
|
+
const reset = () => {
|
|
3268
3489
|
sstWindowListenerMap.clear();
|
|
3269
|
-
sstIntervalIdMap.clear();
|
|
3270
|
-
sstTimeoutIdMap.clear();
|
|
3271
3490
|
sstDocumentListenerMap.clear();
|
|
3272
3491
|
sstOnClickHandler = null;
|
|
3273
3492
|
};
|
|
3274
3493
|
/**
|
|
3275
|
-
*
|
|
3276
|
-
*
|
|
3277
|
-
*
|
|
3278
|
-
*
|
|
3279
|
-
*
|
|
3494
|
+
* NOTE:
|
|
3495
|
+
* 1. about timer(events & properties should record & rebuild at all modes, exclude default mode)
|
|
3496
|
+
* 2. record maybe call twice when unmount prerender, keep-alive app manually with umd mode
|
|
3497
|
+
* 4 modes: default-mode、umd-mode、prerender、keep-alive
|
|
3498
|
+
* Solution:
|
|
3499
|
+
* 1. default-mode(normal): clear events & timers, not record & rebuild anything
|
|
3500
|
+
* 2. umd-mode(normal): not clear timers, record & rebuild events
|
|
3501
|
+
* 3. prerender/keep-alive(default, umd): not clear timers, record & rebuild events
|
|
3280
3502
|
*/
|
|
3281
|
-
const
|
|
3503
|
+
const record = () => {
|
|
3282
3504
|
// record window event
|
|
3283
3505
|
eventListenerMap.forEach((listenerList, type) => {
|
|
3284
3506
|
if (listenerList.size) {
|
|
3285
3507
|
sstWindowListenerMap.set(type, new Set(listenerList));
|
|
3286
3508
|
}
|
|
3287
3509
|
});
|
|
3288
|
-
// record timers
|
|
3289
|
-
if (intervalIdMap.size) {
|
|
3290
|
-
sstIntervalIdMap = new Map(intervalIdMap);
|
|
3291
|
-
}
|
|
3292
|
-
if (timeoutIdMap.size) {
|
|
3293
|
-
sstTimeoutIdMap = new Map(timeoutIdMap);
|
|
3294
|
-
}
|
|
3295
3510
|
// record onclick handler
|
|
3296
|
-
sstOnClickHandler = documentClickListMap.get(appName);
|
|
3511
|
+
sstOnClickHandler = sstOnClickHandler || documentClickListMap.get(appName);
|
|
3297
3512
|
// record document event
|
|
3298
3513
|
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
3299
3514
|
if (documentAppListenersMap) {
|
|
@@ -3305,20 +3520,13 @@ function effect(appName, microAppWindow) {
|
|
|
3305
3520
|
}
|
|
3306
3521
|
};
|
|
3307
3522
|
// rebuild event and timer before remount app
|
|
3308
|
-
const
|
|
3523
|
+
const rebuild = () => {
|
|
3309
3524
|
// rebuild window event
|
|
3310
3525
|
sstWindowListenerMap.forEach((listenerList, type) => {
|
|
3311
3526
|
for (const listener of listenerList) {
|
|
3312
3527
|
microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
|
|
3313
3528
|
}
|
|
3314
3529
|
});
|
|
3315
|
-
// rebuild timer
|
|
3316
|
-
sstIntervalIdMap.forEach((info) => {
|
|
3317
|
-
microAppWindow.setInterval(info.handler, info.timeout, ...info.args);
|
|
3318
|
-
});
|
|
3319
|
-
sstTimeoutIdMap.forEach((info) => {
|
|
3320
|
-
microAppWindow.setTimeout(info.handler, info.timeout, ...info.args);
|
|
3321
|
-
});
|
|
3322
3530
|
// rebuild onclick event
|
|
3323
3531
|
sstOnClickHandler && documentClickListMap.set(appName, sstOnClickHandler);
|
|
3324
3532
|
/**
|
|
@@ -3332,10 +3540,10 @@ function effect(appName, microAppWindow) {
|
|
|
3332
3540
|
}
|
|
3333
3541
|
});
|
|
3334
3542
|
removeDomScope();
|
|
3335
|
-
|
|
3543
|
+
reset();
|
|
3336
3544
|
};
|
|
3337
3545
|
// release all event listener & interval & timeout when unmount app
|
|
3338
|
-
const
|
|
3546
|
+
const release = ({ umdMode, isPrerender, keepAlive, destroy }) => {
|
|
3339
3547
|
// Clear window binding events
|
|
3340
3548
|
if (eventListenerMap.size) {
|
|
3341
3549
|
eventListenerMap.forEach((listenerList, type) => {
|
|
@@ -3345,17 +3553,15 @@ function effect(appName, microAppWindow) {
|
|
|
3345
3553
|
});
|
|
3346
3554
|
eventListenerMap.clear();
|
|
3347
3555
|
}
|
|
3348
|
-
//
|
|
3349
|
-
if (
|
|
3556
|
+
// default mode(not keep-alive or isPrerender)
|
|
3557
|
+
if ((!umdMode && !keepAlive && !isPrerender) || destroy) {
|
|
3350
3558
|
intervalIdMap.forEach((_, intervalId) => {
|
|
3351
3559
|
rawClearInterval.call(rawWindow, intervalId);
|
|
3352
3560
|
});
|
|
3353
|
-
intervalIdMap.clear();
|
|
3354
|
-
}
|
|
3355
|
-
if (timeoutIdMap.size) {
|
|
3356
3561
|
timeoutIdMap.forEach((_, timeoutId) => {
|
|
3357
3562
|
rawClearTimeout.call(rawWindow, timeoutId);
|
|
3358
3563
|
});
|
|
3564
|
+
intervalIdMap.clear();
|
|
3359
3565
|
timeoutIdMap.clear();
|
|
3360
3566
|
}
|
|
3361
3567
|
// Clear the function bound by micro application through document.onclick
|
|
@@ -3372,9 +3578,10 @@ function effect(appName, microAppWindow) {
|
|
|
3372
3578
|
}
|
|
3373
3579
|
};
|
|
3374
3580
|
return {
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3581
|
+
reset,
|
|
3582
|
+
record,
|
|
3583
|
+
rebuild,
|
|
3584
|
+
release,
|
|
3378
3585
|
};
|
|
3379
3586
|
}
|
|
3380
3587
|
|
|
@@ -3569,25 +3776,29 @@ function addHistoryListener(appName) {
|
|
|
3569
3776
|
* 1. unmount app & hidden keep-alive app will not receive popstate event
|
|
3570
3777
|
* 2. filter out onlyForBrowser
|
|
3571
3778
|
*/
|
|
3572
|
-
if (getActiveApps({
|
|
3779
|
+
if (getActiveApps({
|
|
3780
|
+
excludeHiddenApp: true,
|
|
3781
|
+
excludePreRender: true,
|
|
3782
|
+
}).includes(appName) &&
|
|
3573
3783
|
!e.onlyForBrowser) {
|
|
3574
3784
|
const microPath = getMicroPathFromURL(appName);
|
|
3575
3785
|
const app = appInstanceMap.get(appName);
|
|
3576
3786
|
const proxyWindow = app.sandBox.proxyWindow;
|
|
3787
|
+
const microAppWindow = app.sandBox.microAppWindow;
|
|
3577
3788
|
let isHashChange = false;
|
|
3578
3789
|
// for hashChangeEvent
|
|
3579
3790
|
const oldHref = proxyWindow.location.href;
|
|
3580
3791
|
// Do not attach micro state to url when microPath is empty
|
|
3581
3792
|
if (microPath) {
|
|
3582
3793
|
const oldHash = proxyWindow.location.hash;
|
|
3583
|
-
updateMicroLocation(appName, microPath,
|
|
3794
|
+
updateMicroLocation(appName, microPath, microAppWindow.location);
|
|
3584
3795
|
isHashChange = proxyWindow.location.hash !== oldHash;
|
|
3585
3796
|
}
|
|
3586
3797
|
// dispatch formatted popStateEvent to child
|
|
3587
|
-
dispatchPopStateEventToMicroApp(appName, proxyWindow);
|
|
3798
|
+
dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow);
|
|
3588
3799
|
// dispatch formatted hashChangeEvent to child when hash change
|
|
3589
3800
|
if (isHashChange)
|
|
3590
|
-
dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref);
|
|
3801
|
+
dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref);
|
|
3591
3802
|
// clear element scope before trigger event of next app
|
|
3592
3803
|
removeDomScope();
|
|
3593
3804
|
}
|
|
@@ -3603,10 +3814,9 @@ function addHistoryListener(appName) {
|
|
|
3603
3814
|
* @param proxyWindow sandbox window
|
|
3604
3815
|
* @param eventState history.state
|
|
3605
3816
|
*/
|
|
3606
|
-
function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
|
|
3607
|
-
// create PopStateEvent named popstate-appName with sub app state
|
|
3608
|
-
const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName) });
|
|
3817
|
+
function dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow) {
|
|
3609
3818
|
/**
|
|
3819
|
+
* TODO: test
|
|
3610
3820
|
* angular14 takes e.type as type judgment
|
|
3611
3821
|
* when e.type is popstate-appName popstate event will be invalid
|
|
3612
3822
|
*/
|
|
@@ -3616,7 +3826,14 @@ function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
|
|
|
3616
3826
|
// configurable: true,
|
|
3617
3827
|
// enumerable: true,
|
|
3618
3828
|
// })
|
|
3619
|
-
|
|
3829
|
+
// create PopStateEvent named popstate-appName with sub app state
|
|
3830
|
+
const newPopStateEvent = new PopStateEvent(formatEventName('popstate', appName), { state: getMicroState(appName) });
|
|
3831
|
+
if (isIframeSandbox(appName)) {
|
|
3832
|
+
microAppWindow.dispatchEvent(newPopStateEvent);
|
|
3833
|
+
}
|
|
3834
|
+
else {
|
|
3835
|
+
globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
|
|
3836
|
+
}
|
|
3620
3837
|
// call function window.onpopstate if it exists
|
|
3621
3838
|
isFunction(proxyWindow.onpopstate) && proxyWindow.onpopstate(newPopStateEvent);
|
|
3622
3839
|
}
|
|
@@ -3626,12 +3843,17 @@ function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
|
|
|
3626
3843
|
* @param proxyWindow sandbox window
|
|
3627
3844
|
* @param oldHref old href
|
|
3628
3845
|
*/
|
|
3629
|
-
function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
|
|
3630
|
-
const newHashChangeEvent = new HashChangeEvent(formatEventName
|
|
3846
|
+
function dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref) {
|
|
3847
|
+
const newHashChangeEvent = new HashChangeEvent(formatEventName('hashchange', appName), {
|
|
3631
3848
|
newURL: proxyWindow.location.href,
|
|
3632
3849
|
oldURL: oldHref,
|
|
3633
3850
|
});
|
|
3634
|
-
|
|
3851
|
+
if (isIframeSandbox(appName)) {
|
|
3852
|
+
microAppWindow.dispatchEvent(newHashChangeEvent);
|
|
3853
|
+
}
|
|
3854
|
+
else {
|
|
3855
|
+
globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
|
|
3856
|
+
}
|
|
3635
3857
|
// call function window.onhashchange if it exists
|
|
3636
3858
|
isFunction(proxyWindow.onhashchange) && proxyWindow.onhashchange(newHashChangeEvent);
|
|
3637
3859
|
}
|
|
@@ -3683,22 +3905,26 @@ function createMicroHistory(appName, microLocation) {
|
|
|
3683
3905
|
const rawHistory = globalEnv.rawWindow.history;
|
|
3684
3906
|
function getMicroHistoryMethod(methodName) {
|
|
3685
3907
|
return function (...rests) {
|
|
3908
|
+
var _a, _b, _c;
|
|
3909
|
+
// TODO: 测试iframe的URL兼容isURL的情况
|
|
3686
3910
|
if (isString(rests[2]) || isURL(rests[2])) {
|
|
3687
3911
|
const targetLocation = createURL(rests[2], microLocation.href);
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
updateMicroLocation(appName, targetFullPath, microLocation);
|
|
3693
|
-
}
|
|
3694
|
-
return void 0;
|
|
3912
|
+
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
|
|
3913
|
+
navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
|
|
3914
|
+
if (targetFullPath !== microLocation.fullPath) {
|
|
3915
|
+
updateMicroLocation(appName, targetFullPath, microLocation);
|
|
3695
3916
|
}
|
|
3917
|
+
(_c = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : (_b = _a.sandBox).updateIframeBase) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
3918
|
+
}
|
|
3919
|
+
else {
|
|
3920
|
+
nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
|
|
3696
3921
|
}
|
|
3697
|
-
nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
|
|
3698
3922
|
};
|
|
3699
3923
|
}
|
|
3700
3924
|
const pushState = getMicroHistoryMethod('pushState');
|
|
3701
3925
|
const replaceState = getMicroHistoryMethod('replaceState');
|
|
3926
|
+
if (isIframeSandbox(appName))
|
|
3927
|
+
return { pushState, replaceState };
|
|
3702
3928
|
return new Proxy(rawHistory, {
|
|
3703
3929
|
get(target, key) {
|
|
3704
3930
|
if (key === 'state') {
|
|
@@ -3710,8 +3936,7 @@ function createMicroHistory(appName, microLocation) {
|
|
|
3710
3936
|
else if (key === 'replaceState') {
|
|
3711
3937
|
return replaceState;
|
|
3712
3938
|
}
|
|
3713
|
-
|
|
3714
|
-
return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'HISTORY') : rawValue;
|
|
3939
|
+
return bindFunctionToRawTarget(Reflect.get(target, key), target, 'HISTORY');
|
|
3715
3940
|
},
|
|
3716
3941
|
set(target, key, value) {
|
|
3717
3942
|
Reflect.set(target, key, value);
|
|
@@ -3756,6 +3981,7 @@ function navigateWithNativeEvent(appName, methodName, result, onlyForBrowser, st
|
|
|
3756
3981
|
if (isEffectiveApp(appName)) {
|
|
3757
3982
|
const rawLocation = globalEnv.rawWindow.location;
|
|
3758
3983
|
const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
|
|
3984
|
+
// oldHref use for hashChangeEvent of base app
|
|
3759
3985
|
const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
|
|
3760
3986
|
// navigate with native history method
|
|
3761
3987
|
nativeHistoryNavigate(appName, methodName, result.fullPath, state, title);
|
|
@@ -3864,8 +4090,9 @@ function createRouterApi() {
|
|
|
3864
4090
|
const microLocation = app.sandBox.proxyWindow.location;
|
|
3865
4091
|
const targetLocation = createURL(to.path, microLocation.href);
|
|
3866
4092
|
// Only get path data, even if the origin is different from microApp
|
|
4093
|
+
const currentFullPath = microLocation.pathname + microLocation.search + microLocation.hash;
|
|
3867
4094
|
const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
|
|
3868
|
-
if (
|
|
4095
|
+
if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
|
|
3869
4096
|
const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
|
|
3870
4097
|
navigateWithRawHistory(appName, methodName, targetLocation, to.state);
|
|
3871
4098
|
}
|
|
@@ -4014,8 +4241,7 @@ function createRouterApi() {
|
|
|
4014
4241
|
baseRouterProxy = new Proxy(baseRouter, {
|
|
4015
4242
|
get(target, key) {
|
|
4016
4243
|
removeDomScope();
|
|
4017
|
-
|
|
4018
|
-
return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'BASEROUTER') : rawValue;
|
|
4244
|
+
return bindFunctionToRawTarget(Reflect.get(target, key), target, 'BASEROUTER');
|
|
4019
4245
|
},
|
|
4020
4246
|
set(target, key, value) {
|
|
4021
4247
|
Reflect.set(target, key, value);
|
|
@@ -4043,9 +4269,118 @@ function createRouterApi() {
|
|
|
4043
4269
|
}
|
|
4044
4270
|
const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
|
|
4045
4271
|
|
|
4046
|
-
const
|
|
4272
|
+
const escape2RawWindowKeys = [
|
|
4273
|
+
'getComputedStyle',
|
|
4274
|
+
'visualViewport',
|
|
4275
|
+
'matchMedia',
|
|
4276
|
+
// 'DOMParser',
|
|
4277
|
+
'ResizeObserver',
|
|
4278
|
+
'IntersectionObserver',
|
|
4279
|
+
];
|
|
4280
|
+
const escape2RawWindowRegExpKeys = [
|
|
4281
|
+
/animationFrame$/i,
|
|
4282
|
+
/mutationObserver$/i,
|
|
4283
|
+
/height$|width$/i,
|
|
4284
|
+
/offset$/i,
|
|
4285
|
+
// /event$/i,
|
|
4286
|
+
/^screen/i,
|
|
4287
|
+
/^scroll/i,
|
|
4288
|
+
/X$|Y$/,
|
|
4289
|
+
];
|
|
4290
|
+
const scopeIframeWindowOnEvent = [
|
|
4291
|
+
'onload',
|
|
4292
|
+
'onbeforeunload',
|
|
4293
|
+
'onunload',
|
|
4294
|
+
];
|
|
4295
|
+
const scopeIframeWindowEvent = [
|
|
4296
|
+
'hashchange',
|
|
4297
|
+
'popstate',
|
|
4298
|
+
'DOMContentLoaded',
|
|
4299
|
+
'load',
|
|
4300
|
+
'beforeunload',
|
|
4301
|
+
'unload',
|
|
4302
|
+
].concat(microAppCustomEvent);
|
|
4303
|
+
const scopeIframeDocumentEvent = [
|
|
4304
|
+
'DOMContentLoaded',
|
|
4305
|
+
'readystatechange',
|
|
4306
|
+
];
|
|
4307
|
+
const scopeIframeDocumentOnEvent = [
|
|
4308
|
+
'onreadystatechange',
|
|
4309
|
+
];
|
|
4310
|
+
const uniqueDocumentElement = [
|
|
4311
|
+
'body',
|
|
4312
|
+
'head',
|
|
4313
|
+
'html',
|
|
4314
|
+
'title',
|
|
4315
|
+
];
|
|
4316
|
+
const hijackMicroLocationKeys = [
|
|
4317
|
+
'host',
|
|
4318
|
+
'hostname',
|
|
4319
|
+
'port',
|
|
4320
|
+
'protocol',
|
|
4321
|
+
'origin',
|
|
4322
|
+
];
|
|
4323
|
+
// 有shadowRoot则代理到shadowRoot否则代理到原生document上 (属性)
|
|
4324
|
+
const proxy2RawDocOrShadowKeys = [
|
|
4325
|
+
'childElementCount',
|
|
4326
|
+
'children',
|
|
4327
|
+
'firstElementChild',
|
|
4328
|
+
'firstChild',
|
|
4329
|
+
'lastElementChild',
|
|
4330
|
+
'activeElement',
|
|
4331
|
+
'fullscreenElement',
|
|
4332
|
+
'pictureInPictureElement',
|
|
4333
|
+
'pointerLockElement',
|
|
4334
|
+
'styleSheets',
|
|
4335
|
+
];
|
|
4336
|
+
// 有shadowRoot则代理到shadowRoot否则代理到原生document上 (方法)
|
|
4337
|
+
const proxy2RawDocOrShadowMethods = [
|
|
4338
|
+
'append',
|
|
4339
|
+
'contains',
|
|
4340
|
+
'replaceChildren',
|
|
4341
|
+
'getSelection',
|
|
4342
|
+
'elementFromPoint',
|
|
4343
|
+
'elementsFromPoint',
|
|
4344
|
+
'getAnimations',
|
|
4345
|
+
];
|
|
4346
|
+
// 直接代理到原生document上 (属性)
|
|
4347
|
+
const proxy2RawDocumentKeys = [
|
|
4348
|
+
'characterSet',
|
|
4349
|
+
'compatMode',
|
|
4350
|
+
'contentType',
|
|
4351
|
+
'designMode',
|
|
4352
|
+
'dir',
|
|
4353
|
+
'doctype',
|
|
4354
|
+
'embeds',
|
|
4355
|
+
'fullscreenEnabled',
|
|
4356
|
+
'hidden',
|
|
4357
|
+
'implementation',
|
|
4358
|
+
'lastModified',
|
|
4359
|
+
'pictureInPictureEnabled',
|
|
4360
|
+
'plugins',
|
|
4361
|
+
'readyState',
|
|
4362
|
+
'referrer',
|
|
4363
|
+
'visibilityState',
|
|
4364
|
+
'fonts',
|
|
4365
|
+
];
|
|
4366
|
+
// 直接代理到原生document上 (方法)
|
|
4367
|
+
const proxy2RawDocumentMethods = [
|
|
4368
|
+
'execCommand',
|
|
4369
|
+
'createRange',
|
|
4370
|
+
'exitFullscreen',
|
|
4371
|
+
'exitPictureInPicture',
|
|
4372
|
+
'getElementsByTagNameNS',
|
|
4373
|
+
'hasFocus',
|
|
4374
|
+
'prepend',
|
|
4375
|
+
];
|
|
4376
|
+
const globalPropertyList = [
|
|
4377
|
+
'window',
|
|
4378
|
+
'self',
|
|
4379
|
+
'globalThis'
|
|
4380
|
+
];
|
|
4381
|
+
|
|
4047
4382
|
// origin is readonly, so we ignore when updateMicroLocation
|
|
4048
|
-
const locationKeys = [
|
|
4383
|
+
const locationKeys = ['href', 'pathname', 'search', 'hash', 'host', 'hostname', 'port', 'protocol', 'search'];
|
|
4049
4384
|
// origin, fullPath is necessary for guardLocation
|
|
4050
4385
|
const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
|
|
4051
4386
|
/**
|
|
@@ -4053,19 +4388,27 @@ const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
|
|
|
4053
4388
|
* MDN https://developer.mozilla.org/en-US/docs/Web/API/Location
|
|
4054
4389
|
* @param appName app name
|
|
4055
4390
|
* @param url app url
|
|
4391
|
+
* @param microAppWindow iframeWindow, iframe only
|
|
4392
|
+
* @param childStaticLocation real child location info, iframe only
|
|
4393
|
+
* @param browserHost host of browser, iframe only
|
|
4394
|
+
* @param childHost host of child app, iframe only
|
|
4056
4395
|
*/
|
|
4057
|
-
function createMicroLocation(appName, url) {
|
|
4396
|
+
function createMicroLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost) {
|
|
4058
4397
|
const rawWindow = globalEnv.rawWindow;
|
|
4059
4398
|
const rawLocation = rawWindow.location;
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4399
|
+
const isIframe = !!microAppWindow;
|
|
4400
|
+
/**
|
|
4401
|
+
* withLocation is microLocation for with sandbox
|
|
4402
|
+
* it is globally unique for child app
|
|
4403
|
+
*/
|
|
4404
|
+
const withLocation = createURL(url);
|
|
4405
|
+
/**
|
|
4406
|
+
* In iframe, jump through raw iframeLocation will cause microAppWindow.location reset
|
|
4407
|
+
* So we get location dynamically
|
|
4408
|
+
*/
|
|
4409
|
+
function getTarget() {
|
|
4410
|
+
return isIframe ? microAppWindow.location : withLocation;
|
|
4411
|
+
}
|
|
4069
4412
|
/**
|
|
4070
4413
|
* Common handler for href, assign, replace
|
|
4071
4414
|
* It is mainly used to deal with special scenes about hash
|
|
@@ -4073,10 +4416,10 @@ function createMicroLocation(appName, url) {
|
|
|
4073
4416
|
* @param methodName pushState/replaceState
|
|
4074
4417
|
* @returns origin value or formatted value
|
|
4075
4418
|
*/
|
|
4076
|
-
|
|
4077
|
-
const targetLocation = createURL(value,
|
|
4419
|
+
function commonHandler(value, methodName) {
|
|
4420
|
+
const targetLocation = createURL(value, proxyLocation.href);
|
|
4078
4421
|
// Even if the origin is the same, developers still have the possibility of want to jump to a new page
|
|
4079
|
-
if (targetLocation.origin ===
|
|
4422
|
+
if (targetLocation.origin === proxyLocation.origin) {
|
|
4080
4423
|
const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
|
|
4081
4424
|
/**
|
|
4082
4425
|
* change hash with location.href will not trigger the browser reload
|
|
@@ -4085,10 +4428,10 @@ function createMicroLocation(appName, url) {
|
|
|
4085
4428
|
* 1. if child app only change hash, it should not trigger browser reload
|
|
4086
4429
|
* 2. if address is same and has hash, it should not add route stack
|
|
4087
4430
|
*/
|
|
4088
|
-
if (targetLocation.pathname ===
|
|
4089
|
-
targetLocation.search ===
|
|
4431
|
+
if (targetLocation.pathname === proxyLocation.pathname &&
|
|
4432
|
+
targetLocation.search === proxyLocation.search) {
|
|
4090
4433
|
let oldHref = null;
|
|
4091
|
-
if (targetLocation.hash !==
|
|
4434
|
+
if (targetLocation.hash !== proxyLocation.hash) {
|
|
4092
4435
|
if (setMicroPathResult.isAttach2Hash)
|
|
4093
4436
|
oldHref = rawLocation.href;
|
|
4094
4437
|
nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
|
|
@@ -4097,35 +4440,22 @@ function createMicroLocation(appName, url) {
|
|
|
4097
4440
|
dispatchNativeEvent(appName, false, oldHref);
|
|
4098
4441
|
}
|
|
4099
4442
|
else {
|
|
4100
|
-
|
|
4443
|
+
reload();
|
|
4101
4444
|
}
|
|
4102
4445
|
return void 0;
|
|
4103
4446
|
/**
|
|
4104
4447
|
* when baseApp is hash router, address change of child can not reload browser
|
|
4105
|
-
* so we imitate behavior of browser (reload)
|
|
4448
|
+
* so we imitate behavior of browser (reload) manually
|
|
4106
4449
|
*/
|
|
4107
4450
|
}
|
|
4108
4451
|
else if (setMicroPathResult.isAttach2Hash) {
|
|
4109
4452
|
nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
|
|
4110
|
-
|
|
4453
|
+
reload();
|
|
4111
4454
|
return void 0;
|
|
4112
4455
|
}
|
|
4113
|
-
|
|
4456
|
+
return setMicroPathResult.fullPath;
|
|
4114
4457
|
}
|
|
4115
4458
|
return value;
|
|
4116
|
-
};
|
|
4117
|
-
/**
|
|
4118
|
-
* create location PropertyDescriptor (href, pathname, search, hash)
|
|
4119
|
-
* @param key property name
|
|
4120
|
-
* @param setter setter of location property
|
|
4121
|
-
*/
|
|
4122
|
-
function createPropertyDescriptor(getter, setter) {
|
|
4123
|
-
return {
|
|
4124
|
-
enumerable: true,
|
|
4125
|
-
configurable: true,
|
|
4126
|
-
get: getter,
|
|
4127
|
-
set: setter,
|
|
4128
|
-
};
|
|
4129
4459
|
}
|
|
4130
4460
|
/**
|
|
4131
4461
|
* common handler for location.pathname & location.search
|
|
@@ -4135,7 +4465,7 @@ function createMicroLocation(appName, url) {
|
|
|
4135
4465
|
function handleForPathNameAndSearch(targetPath, key) {
|
|
4136
4466
|
const targetLocation = createURL(targetPath, url);
|
|
4137
4467
|
// When the browser url has a hash value, the same pathname/search will not refresh browser
|
|
4138
|
-
if (targetLocation[key] ===
|
|
4468
|
+
if (targetLocation[key] === proxyLocation[key] && proxyLocation.hash) {
|
|
4139
4469
|
// The href has not changed, not need to dispatch hashchange event
|
|
4140
4470
|
dispatchNativeEvent(appName, false);
|
|
4141
4471
|
}
|
|
@@ -4146,58 +4476,96 @@ function createMicroLocation(appName, url) {
|
|
|
4146
4476
|
* pathname: /path ==> /path#hash, /path ==> /path?query
|
|
4147
4477
|
* search: ?query ==> ?query#hash
|
|
4148
4478
|
*/
|
|
4149
|
-
nativeHistoryNavigate(appName, targetLocation[key] ===
|
|
4150
|
-
|
|
4479
|
+
nativeHistoryNavigate(appName, targetLocation[key] === proxyLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
|
|
4480
|
+
reload();
|
|
4151
4481
|
}
|
|
4152
4482
|
}
|
|
4153
|
-
function rawReload() {
|
|
4154
|
-
isEffectiveApp(appName) && rawLocation.reload();
|
|
4155
|
-
}
|
|
4156
|
-
/**
|
|
4157
|
-
* Special processing for four keys: href, pathname, search and hash
|
|
4158
|
-
* They take values from shadowLocation, and require special operations when assigning values
|
|
4159
|
-
*/
|
|
4160
|
-
rawDefineProperties(microLocation, {
|
|
4161
|
-
href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
|
|
4162
|
-
if (isEffectiveApp(appName)) {
|
|
4163
|
-
const targetPath = commonHandler(value, 'pushState');
|
|
4164
|
-
if (targetPath)
|
|
4165
|
-
rawLocation.href = targetPath;
|
|
4166
|
-
}
|
|
4167
|
-
}),
|
|
4168
|
-
pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
|
|
4169
|
-
const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
|
|
4170
|
-
handleForPathNameAndSearch(targetPath, 'pathname');
|
|
4171
|
-
}),
|
|
4172
|
-
search: createPropertyDescriptor(() => shadowLocation.search, (value) => {
|
|
4173
|
-
const targetPath = shadowLocation.pathname + ('?' + value).replace(/^\?+/, '?') + shadowLocation.hash;
|
|
4174
|
-
handleForPathNameAndSearch(targetPath, 'search');
|
|
4175
|
-
}),
|
|
4176
|
-
hash: createPropertyDescriptor(() => shadowLocation.hash, (value) => {
|
|
4177
|
-
const targetPath = shadowLocation.pathname + shadowLocation.search + ('#' + value).replace(/^#+/, '#');
|
|
4178
|
-
const targetLocation = createURL(targetPath, url);
|
|
4179
|
-
// The same hash will not trigger popStateEvent
|
|
4180
|
-
if (targetLocation.hash !== shadowLocation.hash) {
|
|
4181
|
-
navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
|
|
4182
|
-
}
|
|
4183
|
-
}),
|
|
4184
|
-
fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
|
|
4185
|
-
});
|
|
4186
4483
|
const createLocationMethod = (locationMethodName) => {
|
|
4187
4484
|
return function (value) {
|
|
4188
4485
|
if (isEffectiveApp(appName)) {
|
|
4189
4486
|
const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
|
|
4190
|
-
if (targetPath)
|
|
4191
|
-
|
|
4487
|
+
if (targetPath) {
|
|
4488
|
+
// Same as href, complete targetPath with browser origin in vite env
|
|
4489
|
+
rawLocation[locationMethodName](createURL(targetPath, rawLocation.origin).href);
|
|
4490
|
+
}
|
|
4192
4491
|
}
|
|
4193
4492
|
};
|
|
4194
4493
|
};
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4494
|
+
const assign = createLocationMethod('assign');
|
|
4495
|
+
const replace = createLocationMethod('replace');
|
|
4496
|
+
const reload = (forcedReload) => rawLocation.reload(forcedReload);
|
|
4497
|
+
rawDefineProperty(getTarget(), 'fullPath', {
|
|
4498
|
+
enumerable: true,
|
|
4499
|
+
configurable: true,
|
|
4500
|
+
get: () => proxyLocation.pathname + proxyLocation.search + proxyLocation.hash,
|
|
4501
|
+
});
|
|
4502
|
+
/**
|
|
4503
|
+
* location.assign/replace is readonly, cannot be proxy, so we use empty object as proxy target
|
|
4504
|
+
*/
|
|
4505
|
+
const proxyLocation = new Proxy({}, {
|
|
4506
|
+
get: (_, key) => {
|
|
4507
|
+
const target = getTarget();
|
|
4508
|
+
if (isIframe) {
|
|
4509
|
+
// host hostname port protocol
|
|
4510
|
+
if (hijackMicroLocationKeys.includes(key)) {
|
|
4511
|
+
return childStaticLocation[key];
|
|
4512
|
+
}
|
|
4513
|
+
if (key === 'href') {
|
|
4514
|
+
// do not use target, because target may be deleted
|
|
4515
|
+
return target[key].replace(browserHost, childHost);
|
|
4516
|
+
}
|
|
4517
|
+
}
|
|
4518
|
+
if (key === 'assign')
|
|
4519
|
+
return assign;
|
|
4520
|
+
if (key === 'replace')
|
|
4521
|
+
return replace;
|
|
4522
|
+
if (key === 'reload')
|
|
4523
|
+
return reload;
|
|
4524
|
+
if (key === 'self')
|
|
4525
|
+
return target;
|
|
4526
|
+
return bindFunctionToRawTarget(Reflect.get(target, key), target, 'LOCATION');
|
|
4527
|
+
},
|
|
4528
|
+
set: (_, key, value) => {
|
|
4529
|
+
if (isEffectiveApp(appName)) {
|
|
4530
|
+
const target = getTarget();
|
|
4531
|
+
if (key === 'href') {
|
|
4532
|
+
const targetPath = commonHandler(value, 'pushState');
|
|
4533
|
+
/**
|
|
4534
|
+
* In vite, targetPath without origin will be completed with child origin
|
|
4535
|
+
* So we use browser origin to complete targetPath to avoid this problem
|
|
4536
|
+
* But, why child app can affect browser jump?
|
|
4537
|
+
* Guess(need check):
|
|
4538
|
+
* 1. vite records the origin when init
|
|
4539
|
+
* 2. listen for browser jump and automatically complete the address
|
|
4540
|
+
*/
|
|
4541
|
+
if (targetPath) {
|
|
4542
|
+
rawLocation.href = createURL(targetPath, rawLocation.origin).href;
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
else if (key === 'pathname') {
|
|
4546
|
+
const targetPath = ('/' + value).replace(/^\/+/, '/') + proxyLocation.search + proxyLocation.hash;
|
|
4547
|
+
handleForPathNameAndSearch(targetPath, 'pathname');
|
|
4548
|
+
}
|
|
4549
|
+
else if (key === 'search') {
|
|
4550
|
+
const targetPath = proxyLocation.pathname + ('?' + value).replace(/^\?+/, '?') + proxyLocation.hash;
|
|
4551
|
+
handleForPathNameAndSearch(targetPath, 'search');
|
|
4552
|
+
}
|
|
4553
|
+
else if (key === 'hash') {
|
|
4554
|
+
const targetPath = proxyLocation.pathname + proxyLocation.search + ('#' + value).replace(/^#+/, '#');
|
|
4555
|
+
const targetLocation = createURL(targetPath, url);
|
|
4556
|
+
// The same hash will not trigger popStateEvent
|
|
4557
|
+
if (targetLocation.hash !== proxyLocation.hash) {
|
|
4558
|
+
navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
4561
|
+
else {
|
|
4562
|
+
Reflect.set(target, key, value);
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
return true;
|
|
4566
|
+
},
|
|
4200
4567
|
});
|
|
4568
|
+
return proxyLocation;
|
|
4201
4569
|
}
|
|
4202
4570
|
/**
|
|
4203
4571
|
* create guardLocation by microLocation, used for router guard
|
|
@@ -4229,17 +4597,17 @@ function autoTriggerNavigationGuard(appName, microLocation) {
|
|
|
4229
4597
|
* @param type auto prevent
|
|
4230
4598
|
*/
|
|
4231
4599
|
function updateMicroLocation(appName, path, microLocation, type) {
|
|
4232
|
-
|
|
4600
|
+
var _a;
|
|
4233
4601
|
// record old values of microLocation to `from`
|
|
4234
4602
|
const from = createGuardLocation(appName, microLocation);
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
microLocation[key] = newLocation[key];
|
|
4603
|
+
const newLocation = createURL(path, microLocation.href);
|
|
4604
|
+
if (isIframeSandbox(appName)) {
|
|
4605
|
+
const microAppWindow = appInstanceMap.get(appName).sandBox.microAppWindow;
|
|
4606
|
+
(_a = microAppWindow.rawReplaceState) === null || _a === void 0 ? void 0 : _a.call(microAppWindow.history, getMicroState(appName), '', newLocation.href);
|
|
4607
|
+
}
|
|
4608
|
+
else {
|
|
4609
|
+
for (const key of locationKeys) {
|
|
4610
|
+
microLocation.self[key] = newLocation[key];
|
|
4243
4611
|
}
|
|
4244
4612
|
}
|
|
4245
4613
|
// update latest values of microLocation to `to`
|
|
@@ -4335,8 +4703,6 @@ function createMicroFetch(url, target) {
|
|
|
4335
4703
|
* When fetch rewrite by baseApp, domScope still active when exec rawWindow.fetch
|
|
4336
4704
|
* If baseApp operate dom in fetch, it will cause error
|
|
4337
4705
|
* The same for XMLHttpRequest, EventSource
|
|
4338
|
-
* e.g.
|
|
4339
|
-
* baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
|
|
4340
4706
|
*/
|
|
4341
4707
|
removeDomScope();
|
|
4342
4708
|
return rawFetch.call(globalEnv.rawWindow, input, init, ...rests);
|
|
@@ -4419,8 +4785,8 @@ function useMicroEventSource() {
|
|
|
4419
4785
|
}
|
|
4420
4786
|
|
|
4421
4787
|
const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
|
|
4422
|
-
const globalPropertyList = ['window', 'self', 'globalThis'];
|
|
4423
|
-
class
|
|
4788
|
+
const globalPropertyList$1 = ['window', 'self', 'globalThis'];
|
|
4789
|
+
class WithSandBox {
|
|
4424
4790
|
constructor(appName, url) {
|
|
4425
4791
|
/**
|
|
4426
4792
|
* Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
|
|
@@ -4471,17 +4837,21 @@ class SandBox {
|
|
|
4471
4837
|
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
|
|
4472
4838
|
}
|
|
4473
4839
|
/**
|
|
4474
|
-
*
|
|
4475
|
-
*
|
|
4840
|
+
* Target: Ensure default mode action exactly same to first time when render again
|
|
4841
|
+
* 1. The following globalKey maybe modified when render, reset them when render again in default mode
|
|
4842
|
+
* 2. Umd mode will not delete any keys during sandBox.stop, ignore umd mode
|
|
4843
|
+
* 3. When sandbox.start called for the first time, it must be the default mode
|
|
4476
4844
|
*/
|
|
4477
4845
|
if (!umdMode) {
|
|
4478
4846
|
this.initGlobalKeysWhenStart(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, disablePatchRequest);
|
|
4479
4847
|
}
|
|
4480
|
-
if (++
|
|
4848
|
+
if (++globalEnv.activeSandbox === 1) {
|
|
4849
|
+
patchElementAndDocument();
|
|
4850
|
+
patchHistory();
|
|
4851
|
+
}
|
|
4852
|
+
if (++WithSandBox.activeCount === 1) {
|
|
4481
4853
|
effectDocumentEvent();
|
|
4482
|
-
patchElementPrototypeMethods();
|
|
4483
4854
|
initEnvOfNestedApp();
|
|
4484
|
-
patchHistory();
|
|
4485
4855
|
}
|
|
4486
4856
|
fixBabelPolyfill6();
|
|
4487
4857
|
}
|
|
@@ -4490,28 +4860,25 @@ class SandBox {
|
|
|
4490
4860
|
* close sandbox and perform some clean up actions
|
|
4491
4861
|
* @param umdMode is umd mode
|
|
4492
4862
|
* @param keepRouteState prevent reset route
|
|
4493
|
-
* @param
|
|
4863
|
+
* @param destroy completely destroy, delete cache resources
|
|
4494
4864
|
* @param clearData clear data from base app
|
|
4495
4865
|
*/
|
|
4496
|
-
stop({ umdMode, keepRouteState,
|
|
4866
|
+
stop({ umdMode, keepRouteState, destroy, clearData, }) {
|
|
4497
4867
|
if (this.active) {
|
|
4498
|
-
|
|
4499
|
-
this.releaseGlobalEffect(clearData);
|
|
4868
|
+
this.recordAndReleaseEffect({ clearData, destroy }, !umdMode || destroy);
|
|
4500
4869
|
if (this.removeHistoryListener) {
|
|
4501
4870
|
this.clearRouteState(keepRouteState);
|
|
4502
4871
|
// release listener of popstate
|
|
4503
4872
|
this.removeHistoryListener();
|
|
4504
4873
|
}
|
|
4505
|
-
if (clearEventSource) {
|
|
4506
|
-
clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
|
|
4507
|
-
}
|
|
4508
4874
|
/**
|
|
4509
4875
|
* NOTE:
|
|
4510
4876
|
* 1. injectedKeys and escapeKeys must be placed at the back
|
|
4511
4877
|
* 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
|
|
4512
4878
|
* 3. umd mode will not delete global keys
|
|
4513
4879
|
*/
|
|
4514
|
-
if (!umdMode) {
|
|
4880
|
+
if (!umdMode || destroy) {
|
|
4881
|
+
clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
|
|
4515
4882
|
this.injectedKeys.forEach((key) => {
|
|
4516
4883
|
Reflect.deleteProperty(this.microAppWindow, key);
|
|
4517
4884
|
});
|
|
@@ -4521,30 +4888,47 @@ class SandBox {
|
|
|
4521
4888
|
});
|
|
4522
4889
|
this.escapeKeys.clear();
|
|
4523
4890
|
}
|
|
4524
|
-
if (--
|
|
4525
|
-
|
|
4526
|
-
releasePatches();
|
|
4891
|
+
if (--globalEnv.activeSandbox === 0) {
|
|
4892
|
+
releasePatchElementAndDocument();
|
|
4527
4893
|
releasePatchHistory();
|
|
4528
4894
|
}
|
|
4895
|
+
if (--WithSandBox.activeCount === 0) {
|
|
4896
|
+
releaseEffectDocumentEvent();
|
|
4897
|
+
}
|
|
4529
4898
|
this.active = false;
|
|
4530
4899
|
}
|
|
4531
4900
|
}
|
|
4532
4901
|
/**
|
|
4533
|
-
*
|
|
4902
|
+
* Record global effect and then release (effect: global event, timeout, data listener)
|
|
4534
4903
|
* Scenes:
|
|
4535
|
-
* 1. unmount of
|
|
4904
|
+
* 1. unmount of default/umd app
|
|
4536
4905
|
* 2. hidden keep-alive app
|
|
4537
4906
|
* 3. after init prerender app
|
|
4538
|
-
* @param
|
|
4907
|
+
* @param options {
|
|
4908
|
+
* @param clearData clear data from base app
|
|
4909
|
+
* @param isPrerender is prerender app
|
|
4910
|
+
* @param keepAlive is keep-alive app
|
|
4911
|
+
* }
|
|
4912
|
+
* @param preventRecord prevent record effect events
|
|
4539
4913
|
*/
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
this.microAppWindow.microApp.clearData();
|
|
4914
|
+
recordAndReleaseEffect(options, preventRecord = false) {
|
|
4915
|
+
if (preventRecord) {
|
|
4916
|
+
this.resetEffectSnapshot();
|
|
4917
|
+
}
|
|
4918
|
+
else {
|
|
4919
|
+
this.recordEffectSnapshot();
|
|
4547
4920
|
}
|
|
4921
|
+
this.releaseGlobalEffect(options);
|
|
4922
|
+
}
|
|
4923
|
+
/**
|
|
4924
|
+
* reset effect snapshot data in default mode or destroy
|
|
4925
|
+
* Scenes:
|
|
4926
|
+
* 1. unmount hidden keep-alive app manually
|
|
4927
|
+
* 2. unmount prerender app manually
|
|
4928
|
+
*/
|
|
4929
|
+
resetEffectSnapshot() {
|
|
4930
|
+
this.effectController.reset();
|
|
4931
|
+
resetDataCenterSnapshot(this.microAppWindow.microApp);
|
|
4548
4932
|
}
|
|
4549
4933
|
/**
|
|
4550
4934
|
* record umd snapshot before the first execution of umdHookMount
|
|
@@ -4555,7 +4939,7 @@ class SandBox {
|
|
|
4555
4939
|
*/
|
|
4556
4940
|
recordEffectSnapshot() {
|
|
4557
4941
|
// this.microAppWindow.__MICRO_APP_UMD_MODE__ = true
|
|
4558
|
-
this.effectController.
|
|
4942
|
+
this.effectController.record();
|
|
4559
4943
|
recordDataCenterSnapshot(this.microAppWindow.microApp);
|
|
4560
4944
|
// this.recordUmdInjectedValues = new Map<PropertyKey, unknown>()
|
|
4561
4945
|
// this.injectedKeys.forEach((key: PropertyKey) => {
|
|
@@ -4567,12 +4951,34 @@ class SandBox {
|
|
|
4567
4951
|
// this.recordUmdInjectedValues!.forEach((value: unknown, key: PropertyKey) => {
|
|
4568
4952
|
// Reflect.set(this.proxyWindow, key, value)
|
|
4569
4953
|
// })
|
|
4570
|
-
this.effectController.
|
|
4954
|
+
this.effectController.rebuild();
|
|
4571
4955
|
rebuildDataCenterSnapshot(this.microAppWindow.microApp);
|
|
4572
4956
|
}
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4957
|
+
/**
|
|
4958
|
+
* clear global event, timeout, data listener
|
|
4959
|
+
* Scenes:
|
|
4960
|
+
* 1. unmount of default/umd app
|
|
4961
|
+
* 2. hidden keep-alive app
|
|
4962
|
+
* 3. after init prerender app
|
|
4963
|
+
* @param clearData clear data from base app
|
|
4964
|
+
* @param isPrerender is prerender app
|
|
4965
|
+
* @param keepAlive is keep-alive app
|
|
4966
|
+
* @param destroy completely destroy
|
|
4967
|
+
*/
|
|
4968
|
+
releaseGlobalEffect({ clearData = false, isPrerender = false, keepAlive = false, destroy = false, }) {
|
|
4969
|
+
var _a, _b, _c;
|
|
4970
|
+
this.effectController.release({
|
|
4971
|
+
umdMode: this.proxyWindow.__MICRO_APP_UMD_MODE__,
|
|
4972
|
+
isPrerender,
|
|
4973
|
+
keepAlive,
|
|
4974
|
+
destroy,
|
|
4975
|
+
});
|
|
4976
|
+
(_a = this.microAppWindow.microApp) === null || _a === void 0 ? void 0 : _a.clearDataListener();
|
|
4977
|
+
(_b = this.microAppWindow.microApp) === null || _b === void 0 ? void 0 : _b.clearGlobalDataListener();
|
|
4978
|
+
if (clearData) {
|
|
4979
|
+
microApp.clearData(this.microAppWindow.__MICRO_APP_NAME__);
|
|
4980
|
+
(_c = this.microAppWindow.microApp) === null || _c === void 0 ? void 0 : _c.clearData();
|
|
4981
|
+
}
|
|
4576
4982
|
}
|
|
4577
4983
|
/**
|
|
4578
4984
|
* get scopeProperties and escapeProperties from plugins & adapter
|
|
@@ -4605,7 +5011,6 @@ class SandBox {
|
|
|
4605
5011
|
createProxyWindow(appName) {
|
|
4606
5012
|
const rawWindow = globalEnv.rawWindow;
|
|
4607
5013
|
const descriptorTargetMap = new Map();
|
|
4608
|
-
// window.xxx will trigger proxy
|
|
4609
5014
|
return new Proxy(this.microAppWindow, {
|
|
4610
5015
|
get: (target, key) => {
|
|
4611
5016
|
throttleDeferForSetAppName(appName);
|
|
@@ -4613,11 +5018,14 @@ class SandBox {
|
|
|
4613
5018
|
(isString(key) && /^__MICRO_APP_/.test(key)) ||
|
|
4614
5019
|
this.scopeProperties.includes(key))
|
|
4615
5020
|
return Reflect.get(target, key);
|
|
4616
|
-
|
|
4617
|
-
return isFunction(rawValue) ? bindFunctionToRawObject(rawWindow, rawValue) : rawValue;
|
|
5021
|
+
return bindFunctionToRawTarget(Reflect.get(rawWindow, key), rawWindow);
|
|
4618
5022
|
},
|
|
4619
5023
|
set: (target, key, value) => {
|
|
4620
5024
|
if (this.active) {
|
|
5025
|
+
/**
|
|
5026
|
+
* TODO:
|
|
5027
|
+
* 1、location域名相同,子应用内部跳转时的处理
|
|
5028
|
+
*/
|
|
4621
5029
|
if (this.adapter.escapeSetterKeyList.includes(key)) {
|
|
4622
5030
|
Reflect.set(rawWindow, key, value);
|
|
4623
5031
|
}
|
|
@@ -4638,15 +5046,15 @@ class SandBox {
|
|
|
4638
5046
|
this.injectedKeys.add(key);
|
|
4639
5047
|
}
|
|
4640
5048
|
else {
|
|
5049
|
+
!Reflect.has(target, key) && this.injectedKeys.add(key);
|
|
4641
5050
|
Reflect.set(target, key, value);
|
|
4642
|
-
this.injectedKeys.add(key);
|
|
4643
5051
|
}
|
|
4644
5052
|
if ((this.escapeProperties.includes(key) ||
|
|
4645
5053
|
(this.adapter.staticEscapeProperties.includes(key) &&
|
|
4646
5054
|
!Reflect.has(rawWindow, key))) &&
|
|
4647
5055
|
!this.scopeProperties.includes(key)) {
|
|
5056
|
+
!Reflect.has(rawWindow, key) && this.escapeKeys.add(key);
|
|
4648
5057
|
Reflect.set(rawWindow, key, value);
|
|
4649
|
-
this.escapeKeys.add(key);
|
|
4650
5058
|
}
|
|
4651
5059
|
}
|
|
4652
5060
|
return true;
|
|
@@ -4694,6 +5102,13 @@ class SandBox {
|
|
|
4694
5102
|
},
|
|
4695
5103
|
});
|
|
4696
5104
|
}
|
|
5105
|
+
// set __MICRO_APP_PRE_RENDER__ state
|
|
5106
|
+
setPreRenderState(state) {
|
|
5107
|
+
this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
|
|
5108
|
+
}
|
|
5109
|
+
markUmdMode(state) {
|
|
5110
|
+
this.microAppWindow.__MICRO_APP_UMD_MODE__ = state;
|
|
5111
|
+
}
|
|
4697
5112
|
/**
|
|
4698
5113
|
* inject global properties to microAppWindow
|
|
4699
5114
|
* @param microAppWindow micro window
|
|
@@ -4708,6 +5123,7 @@ class SandBox {
|
|
|
4708
5123
|
microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
|
|
4709
5124
|
microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
|
|
4710
5125
|
microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
|
|
5126
|
+
microAppWindow.__MICRO_APP_UMD_MODE__ = false;
|
|
4711
5127
|
microAppWindow.rawWindow = globalEnv.rawWindow;
|
|
4712
5128
|
microAppWindow.rawDocument = globalEnv.rawDocument;
|
|
4713
5129
|
microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
|
|
@@ -4725,7 +5141,6 @@ class SandBox {
|
|
|
4725
5141
|
configurable: false,
|
|
4726
5142
|
enumerable: true,
|
|
4727
5143
|
get() {
|
|
4728
|
-
throttleDeferForSetAppName(appName);
|
|
4729
5144
|
// return globalEnv.rawDocument
|
|
4730
5145
|
return proxyDocument;
|
|
4731
5146
|
},
|
|
@@ -4734,7 +5149,6 @@ class SandBox {
|
|
|
4734
5149
|
configurable: false,
|
|
4735
5150
|
enumerable: false,
|
|
4736
5151
|
get() {
|
|
4737
|
-
throttleDeferForSetAppName(appName);
|
|
4738
5152
|
// return globalEnv.rawRootDocument
|
|
4739
5153
|
return MicroDocument;
|
|
4740
5154
|
},
|
|
@@ -4754,7 +5168,7 @@ class SandBox {
|
|
|
4754
5168
|
}
|
|
4755
5169
|
rawDefineProperty(microAppWindow, 'top', this.createDescriptorForMicroAppWindow('top', topValue));
|
|
4756
5170
|
rawDefineProperty(microAppWindow, 'parent', this.createDescriptorForMicroAppWindow('parent', parentValue));
|
|
4757
|
-
globalPropertyList.forEach((key) => {
|
|
5171
|
+
globalPropertyList$1.forEach((key) => {
|
|
4758
5172
|
rawDefineProperty(microAppWindow, key, this.createDescriptorForMicroAppWindow(key, this.proxyWindow));
|
|
4759
5173
|
});
|
|
4760
5174
|
}
|
|
@@ -4780,6 +5194,7 @@ class SandBox {
|
|
|
4780
5194
|
this.setHijackProperty(microAppWindow, appName);
|
|
4781
5195
|
if (!disablePatchRequest)
|
|
4782
5196
|
this.patchRequestApi(microAppWindow, appName, url);
|
|
5197
|
+
this.setScopeProperties(microAppWindow);
|
|
4783
5198
|
}
|
|
4784
5199
|
// set hijack Properties to microAppWindow
|
|
4785
5200
|
setHijackProperty(microAppWindow, appName) {
|
|
@@ -4847,9 +5262,21 @@ class SandBox {
|
|
|
4847
5262
|
},
|
|
4848
5263
|
});
|
|
4849
5264
|
}
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
5265
|
+
/**
|
|
5266
|
+
* Init scope keys to microAppWindow, prevent fall to rawWindow from with(microAppWindow)
|
|
5267
|
+
* like: if (!xxx) {}
|
|
5268
|
+
* NOTE:
|
|
5269
|
+
* 1. Symbol.unscopables cannot affect undefined keys
|
|
5270
|
+
* 2. Doesn't use for window.xxx because it fall to proxyWindow
|
|
5271
|
+
*/
|
|
5272
|
+
setScopeProperties(microAppWindow) {
|
|
5273
|
+
this.scopeProperties.forEach((key) => {
|
|
5274
|
+
Reflect.set(microAppWindow, key, microAppWindow[key]);
|
|
5275
|
+
});
|
|
5276
|
+
}
|
|
5277
|
+
// set location & history for memory router
|
|
5278
|
+
setMicroAppRouter(microAppWindow, appName, url) {
|
|
5279
|
+
const { microLocation, microHistory } = createMicroRouter(appName, url);
|
|
4853
5280
|
rawDefineProperties(microAppWindow, {
|
|
4854
5281
|
location: {
|
|
4855
5282
|
configurable: false,
|
|
@@ -4871,91 +5298,1085 @@ class SandBox {
|
|
|
4871
5298
|
});
|
|
4872
5299
|
}
|
|
4873
5300
|
initRouteState(defaultPage) {
|
|
4874
|
-
initRouteStateWithURL(this.
|
|
5301
|
+
initRouteStateWithURL(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location, defaultPage);
|
|
5302
|
+
}
|
|
5303
|
+
clearRouteState(keepRouteState) {
|
|
5304
|
+
clearRouteStateFromURL(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, this.microAppWindow.location, keepRouteState);
|
|
5305
|
+
}
|
|
5306
|
+
setRouteInfoForKeepAliveApp() {
|
|
5307
|
+
updateBrowserURLWithLocation(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location);
|
|
5308
|
+
}
|
|
5309
|
+
removeRouteInfoForKeepAliveApp() {
|
|
5310
|
+
removeStateAndPathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__);
|
|
5311
|
+
}
|
|
5312
|
+
/**
|
|
5313
|
+
* Format all html elements when init
|
|
5314
|
+
* @param container micro app container
|
|
5315
|
+
*/
|
|
5316
|
+
patchStaticElement(container) {
|
|
5317
|
+
patchElementTree(container, this.microAppWindow.__MICRO_APP_NAME__);
|
|
5318
|
+
}
|
|
5319
|
+
/**
|
|
5320
|
+
* action before exec scripts when mount
|
|
5321
|
+
* Actions:
|
|
5322
|
+
* 1. patch static elements from html
|
|
5323
|
+
* @param container micro app container
|
|
5324
|
+
*/
|
|
5325
|
+
actionBeforeExecScripts(container) {
|
|
5326
|
+
this.patchStaticElement(container);
|
|
5327
|
+
}
|
|
5328
|
+
/**
|
|
5329
|
+
* Create new document and Document
|
|
5330
|
+
*/
|
|
5331
|
+
createProxyDocument(appName) {
|
|
5332
|
+
const rawDocument = globalEnv.rawDocument;
|
|
5333
|
+
const rawRootDocument = globalEnv.rawRootDocument;
|
|
5334
|
+
const createElement = function (tagName, options) {
|
|
5335
|
+
const element = globalEnv.rawCreateElement.call(rawDocument, tagName, options);
|
|
5336
|
+
element.__MICRO_APP_NAME__ = appName;
|
|
5337
|
+
return element;
|
|
5338
|
+
};
|
|
5339
|
+
const proxyDocument = new Proxy(rawDocument, {
|
|
5340
|
+
get: (target, key) => {
|
|
5341
|
+
throttleDeferForSetAppName(appName);
|
|
5342
|
+
throttleDeferForParentNode(proxyDocument);
|
|
5343
|
+
if (key === 'createElement')
|
|
5344
|
+
return createElement;
|
|
5345
|
+
if (key === Symbol.toStringTag)
|
|
5346
|
+
return 'ProxyDocument';
|
|
5347
|
+
if (key === 'defaultView')
|
|
5348
|
+
return this.proxyWindow;
|
|
5349
|
+
return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
|
|
5350
|
+
},
|
|
5351
|
+
set: (target, key, value) => {
|
|
5352
|
+
/**
|
|
5353
|
+
* 1. Fix TypeError: Illegal invocation when set document.title
|
|
5354
|
+
* 2. If the set method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
|
|
5355
|
+
*/
|
|
5356
|
+
Reflect.set(target, key, value);
|
|
5357
|
+
return true;
|
|
5358
|
+
}
|
|
5359
|
+
});
|
|
5360
|
+
class MicroDocument {
|
|
5361
|
+
static [Symbol.hasInstance](target) {
|
|
5362
|
+
let proto = target;
|
|
5363
|
+
while (proto = Object.getPrototypeOf(proto)) {
|
|
5364
|
+
if (proto === MicroDocument.prototype) {
|
|
5365
|
+
return true;
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
return (target === proxyDocument ||
|
|
5369
|
+
target instanceof rawRootDocument);
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
5372
|
+
/**
|
|
5373
|
+
* TIP:
|
|
5374
|
+
* 1. child class __proto__, which represents the inherit of the constructor, always points to the parent class
|
|
5375
|
+
* 2. child class prototype.__proto__, which represents the inherit of methods, always points to parent class prototype
|
|
5376
|
+
* e.g.
|
|
5377
|
+
* class B extends A {}
|
|
5378
|
+
* B.__proto__ === A // true
|
|
5379
|
+
* B.prototype.__proto__ === A.prototype // true
|
|
5380
|
+
*/
|
|
5381
|
+
Object.setPrototypeOf(MicroDocument, rawRootDocument);
|
|
5382
|
+
// Object.create(rawRootDocument.prototype) will cause MicroDocument and proxyDocument methods not same when exec Document.prototype.xxx = xxx in child app
|
|
5383
|
+
Object.setPrototypeOf(MicroDocument.prototype, new Proxy(rawRootDocument.prototype, {
|
|
5384
|
+
get(target, key) {
|
|
5385
|
+
throttleDeferForSetAppName(appName);
|
|
5386
|
+
return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
|
|
5387
|
+
},
|
|
5388
|
+
set(target, key, value) {
|
|
5389
|
+
Reflect.set(target, key, value);
|
|
5390
|
+
return true;
|
|
5391
|
+
}
|
|
5392
|
+
}));
|
|
5393
|
+
return {
|
|
5394
|
+
proxyDocument,
|
|
5395
|
+
MicroDocument,
|
|
5396
|
+
};
|
|
5397
|
+
}
|
|
5398
|
+
}
|
|
5399
|
+
WithSandBox.activeCount = 0; // number of active sandbox
|
|
5400
|
+
|
|
5401
|
+
function patchIframeRoute(appName, microAppWindow, childFullPath) {
|
|
5402
|
+
const microHistory = microAppWindow.history;
|
|
5403
|
+
microAppWindow.rawReplaceState = microHistory.replaceState;
|
|
5404
|
+
assign(microHistory, createMicroHistory(appName, microAppWindow.location));
|
|
5405
|
+
// exec updateMicroLocation after patch microHistory
|
|
5406
|
+
updateMicroLocation(appName, childFullPath, microAppWindow.location, 'prevent');
|
|
5407
|
+
}
|
|
5408
|
+
|
|
5409
|
+
function patchIframeWindow(appName, microAppWindow) {
|
|
5410
|
+
const rawWindow = globalEnv.rawWindow;
|
|
5411
|
+
escape2RawWindowKeys.forEach((key) => {
|
|
5412
|
+
microAppWindow[key] = bindFunctionToRawTarget(rawWindow[key], rawWindow);
|
|
5413
|
+
});
|
|
5414
|
+
Object.getOwnPropertyNames(microAppWindow)
|
|
5415
|
+
.filter((key) => {
|
|
5416
|
+
escape2RawWindowRegExpKeys.some((reg) => {
|
|
5417
|
+
if (reg.test(key) && key in microAppWindow.parent) {
|
|
5418
|
+
if (isFunction(rawWindow[key])) {
|
|
5419
|
+
microAppWindow[key] = bindFunctionToRawTarget(rawWindow[key], rawWindow);
|
|
5420
|
+
}
|
|
5421
|
+
else {
|
|
5422
|
+
const { configurable, enumerable } = Object.getOwnPropertyDescriptor(microAppWindow, key) || {
|
|
5423
|
+
configurable: true,
|
|
5424
|
+
enumerable: true,
|
|
5425
|
+
};
|
|
5426
|
+
if (configurable) {
|
|
5427
|
+
rawDefineProperty(microAppWindow, key, {
|
|
5428
|
+
configurable,
|
|
5429
|
+
enumerable,
|
|
5430
|
+
get: () => rawWindow[key],
|
|
5431
|
+
set: (value) => { rawWindow[key] = value; },
|
|
5432
|
+
});
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
return true;
|
|
5436
|
+
}
|
|
5437
|
+
return false;
|
|
5438
|
+
});
|
|
5439
|
+
return /^on/.test(key) && !scopeIframeWindowOnEvent.includes(key);
|
|
5440
|
+
})
|
|
5441
|
+
.forEach((eventName) => {
|
|
5442
|
+
const { enumerable, writable, set } = Object.getOwnPropertyDescriptor(microAppWindow, eventName) || {
|
|
5443
|
+
enumerable: true,
|
|
5444
|
+
writable: true,
|
|
5445
|
+
};
|
|
5446
|
+
try {
|
|
5447
|
+
/**
|
|
5448
|
+
* 如果设置了iframeWindow上的这些on事件,处理函数会设置到原生window上,但this会绑定到iframeWindow
|
|
5449
|
+
* 获取这些值,则直接从原生window上取
|
|
5450
|
+
* 总结:这些on事件全部都代理到原生window上
|
|
5451
|
+
*
|
|
5452
|
+
* 问题:
|
|
5453
|
+
* 1、如果子应用没有设置,基座设置了on事件,子应用触发事件是会不会执行基座的函数?
|
|
5454
|
+
* 比如 基座定义了 window.onpopstate,子应用执行跳转会不会触发基座的onpopstate函数?
|
|
5455
|
+
*
|
|
5456
|
+
* 2、如果基座已经定义了 window.onpopstate,子应用定义会不会覆盖基座的?
|
|
5457
|
+
* 现在的逻辑看来,是会覆盖的,那么问题1就是 肯定的
|
|
5458
|
+
* TODO: 一些特殊事件onpopstate、onhashchange不代理,放在scopeIframeWindowOnEvent中
|
|
5459
|
+
*/
|
|
5460
|
+
rawDefineProperty(microAppWindow, eventName, {
|
|
5461
|
+
enumerable,
|
|
5462
|
+
configurable: true,
|
|
5463
|
+
get: () => rawWindow[eventName],
|
|
5464
|
+
set: (writable !== null && writable !== void 0 ? writable : !!set) ? (value) => { rawWindow[eventName] = isFunction(value) ? value.bind(microAppWindow) : value; }
|
|
5465
|
+
: undefined,
|
|
5466
|
+
});
|
|
5467
|
+
}
|
|
5468
|
+
catch (e) {
|
|
5469
|
+
logWarn(e, appName);
|
|
5470
|
+
}
|
|
5471
|
+
});
|
|
5472
|
+
return windowEffect(microAppWindow);
|
|
5473
|
+
}
|
|
5474
|
+
function windowEffect(microAppWindow) {
|
|
5475
|
+
const { rawWindow, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
|
|
5476
|
+
const eventListenerMap = new Map();
|
|
5477
|
+
const sstWindowListenerMap = new Map();
|
|
5478
|
+
function getEventTarget(type) {
|
|
5479
|
+
return scopeIframeWindowEvent.includes(type) ? microAppWindow : rawWindow;
|
|
5480
|
+
}
|
|
5481
|
+
microAppWindow.addEventListener = function (type, listener, options) {
|
|
5482
|
+
const listenerList = eventListenerMap.get(type);
|
|
5483
|
+
if (listenerList) {
|
|
5484
|
+
listenerList.add(listener);
|
|
5485
|
+
}
|
|
5486
|
+
else {
|
|
5487
|
+
eventListenerMap.set(type, new Set([listener]));
|
|
5488
|
+
}
|
|
5489
|
+
listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
|
|
5490
|
+
rawAddEventListener.call(getEventTarget(type), type, listener, options);
|
|
5491
|
+
};
|
|
5492
|
+
microAppWindow.removeEventListener = function (type, listener, options) {
|
|
5493
|
+
const listenerList = eventListenerMap.get(type);
|
|
5494
|
+
if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
|
|
5495
|
+
listenerList.delete(listener);
|
|
5496
|
+
}
|
|
5497
|
+
rawRemoveEventListener.call(getEventTarget(type), type, listener, options);
|
|
5498
|
+
};
|
|
5499
|
+
const reset = () => {
|
|
5500
|
+
sstWindowListenerMap.clear();
|
|
5501
|
+
};
|
|
5502
|
+
/**
|
|
5503
|
+
* NOTE:
|
|
5504
|
+
* 1. about timer(events & properties should record & rebuild at all modes, exclude default mode)
|
|
5505
|
+
* 2. record maybe call twice when unmount prerender, keep-alive app manually with umd mode
|
|
5506
|
+
* 4 modes: default-mode、umd-mode、prerender、keep-alive
|
|
5507
|
+
* Solution:
|
|
5508
|
+
* 1. default-mode(normal): clear events & timers, not record & rebuild anything
|
|
5509
|
+
* 2. umd-mode(normal): not clear timers, record & rebuild events
|
|
5510
|
+
* 3. prerender/keep-alive(default, umd): not clear timers, record & rebuild events
|
|
5511
|
+
*
|
|
5512
|
+
* TODO: 现在的 清除、记录和恢复操作分散的太零散,sandbox、create_app中都有分散,将代码再优化一下,集中处理
|
|
5513
|
+
*/
|
|
5514
|
+
const record = () => {
|
|
5515
|
+
// record window event
|
|
5516
|
+
eventListenerMap.forEach((listenerList, type) => {
|
|
5517
|
+
if (listenerList.size) {
|
|
5518
|
+
sstWindowListenerMap.set(type, new Set(listenerList));
|
|
5519
|
+
}
|
|
5520
|
+
});
|
|
5521
|
+
};
|
|
5522
|
+
// rebuild event and timer before remount app
|
|
5523
|
+
const rebuild = () => {
|
|
5524
|
+
// rebuild window event
|
|
5525
|
+
sstWindowListenerMap.forEach((listenerList, type) => {
|
|
5526
|
+
for (const listener of listenerList) {
|
|
5527
|
+
microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
|
|
5528
|
+
}
|
|
5529
|
+
});
|
|
5530
|
+
reset();
|
|
5531
|
+
};
|
|
5532
|
+
const release = () => {
|
|
5533
|
+
// Clear window binding events
|
|
5534
|
+
if (eventListenerMap.size) {
|
|
5535
|
+
eventListenerMap.forEach((listenerList, type) => {
|
|
5536
|
+
for (const listener of listenerList) {
|
|
5537
|
+
rawRemoveEventListener.call(getEventTarget(type), type, listener);
|
|
5538
|
+
}
|
|
5539
|
+
});
|
|
5540
|
+
eventListenerMap.clear();
|
|
5541
|
+
}
|
|
5542
|
+
};
|
|
5543
|
+
return {
|
|
5544
|
+
reset,
|
|
5545
|
+
record,
|
|
5546
|
+
rebuild,
|
|
5547
|
+
release,
|
|
5548
|
+
};
|
|
5549
|
+
}
|
|
5550
|
+
|
|
5551
|
+
/**
|
|
5552
|
+
* TODO:
|
|
5553
|
+
* 1、shadowDOM
|
|
5554
|
+
* 2、重构
|
|
5555
|
+
*/
|
|
5556
|
+
function patchIframeDocument(appName, microAppWindow, proxyLocation) {
|
|
5557
|
+
patchDocumentPrototype(appName, microAppWindow);
|
|
5558
|
+
patchDocumentProperties(appName, microAppWindow, proxyLocation);
|
|
5559
|
+
return documentEffect(appName, microAppWindow);
|
|
5560
|
+
}
|
|
5561
|
+
function patchDocumentPrototype(appName, microAppWindow) {
|
|
5562
|
+
const rawDocument = globalEnv.rawDocument;
|
|
5563
|
+
const microRootDocument = microAppWindow.Document;
|
|
5564
|
+
const microDocument = microAppWindow.document;
|
|
5565
|
+
microRootDocument.prototype.createElement = function createElement(tagName, options) {
|
|
5566
|
+
const element = globalEnv.rawCreateElement.call(this, tagName, options);
|
|
5567
|
+
return updateElementInfo(element, appName);
|
|
5568
|
+
};
|
|
5569
|
+
microRootDocument.prototype.createTextNode = function createTextNode(data) {
|
|
5570
|
+
const element = globalEnv.rawCreateTextNode.call(this, data);
|
|
5571
|
+
return updateElementInfo(element, appName);
|
|
5572
|
+
};
|
|
5573
|
+
function getDefaultRawTarget(target) {
|
|
5574
|
+
return microDocument !== target ? target : rawDocument;
|
|
5575
|
+
}
|
|
5576
|
+
// query element👇
|
|
5577
|
+
function querySelector(selectors) {
|
|
5578
|
+
var _a, _b;
|
|
5579
|
+
if (isUniqueElement(selectors) ||
|
|
5580
|
+
microDocument !== this) {
|
|
5581
|
+
const _this = getDefaultRawTarget(this);
|
|
5582
|
+
return globalEnv.rawQuerySelector.call(_this, selectors);
|
|
5583
|
+
}
|
|
5584
|
+
return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors)) !== null && _b !== void 0 ? _b : null;
|
|
5585
|
+
}
|
|
5586
|
+
function querySelectorAll(selectors) {
|
|
5587
|
+
var _a, _b;
|
|
5588
|
+
if (isUniqueElement(selectors) ||
|
|
5589
|
+
microDocument !== this) {
|
|
5590
|
+
const _this = getDefaultRawTarget(this);
|
|
5591
|
+
return globalEnv.rawQuerySelectorAll.call(_this, selectors);
|
|
5592
|
+
}
|
|
5593
|
+
return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
|
|
5594
|
+
}
|
|
5595
|
+
microRootDocument.prototype.querySelector = querySelector;
|
|
5596
|
+
microRootDocument.prototype.querySelectorAll = querySelectorAll;
|
|
5597
|
+
microRootDocument.prototype.getElementById = function getElementById(key) {
|
|
5598
|
+
const _this = getDefaultRawTarget(this);
|
|
5599
|
+
if (isInvalidQuerySelectorKey(key)) {
|
|
5600
|
+
return globalEnv.rawGetElementById.call(_this, key);
|
|
5601
|
+
}
|
|
5602
|
+
try {
|
|
5603
|
+
return querySelector.call(this, `#${key}`);
|
|
5604
|
+
}
|
|
5605
|
+
catch (_a) {
|
|
5606
|
+
return globalEnv.rawGetElementById.call(_this, key);
|
|
5607
|
+
}
|
|
5608
|
+
};
|
|
5609
|
+
microRootDocument.prototype.getElementsByClassName = function getElementsByClassName(key) {
|
|
5610
|
+
const _this = getDefaultRawTarget(this);
|
|
5611
|
+
if (isInvalidQuerySelectorKey(key)) {
|
|
5612
|
+
return globalEnv.rawGetElementsByClassName.call(_this, key);
|
|
5613
|
+
}
|
|
5614
|
+
try {
|
|
5615
|
+
return querySelectorAll.call(this, `.${key}`);
|
|
5616
|
+
}
|
|
5617
|
+
catch (_a) {
|
|
5618
|
+
return globalEnv.rawGetElementsByClassName.call(_this, key);
|
|
5619
|
+
}
|
|
5620
|
+
};
|
|
5621
|
+
microRootDocument.prototype.getElementsByTagName = function getElementsByTagName(key) {
|
|
5622
|
+
var _a;
|
|
5623
|
+
const _this = getDefaultRawTarget(this);
|
|
5624
|
+
if (isUniqueElement(key) ||
|
|
5625
|
+
isInvalidQuerySelectorKey(key) ||
|
|
5626
|
+
(!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
|
|
5627
|
+
return globalEnv.rawGetElementsByTagName.call(_this, key);
|
|
5628
|
+
}
|
|
5629
|
+
try {
|
|
5630
|
+
return querySelectorAll.call(this, key);
|
|
5631
|
+
}
|
|
5632
|
+
catch (_b) {
|
|
5633
|
+
return globalEnv.rawGetElementsByTagName.call(_this, key);
|
|
5634
|
+
}
|
|
5635
|
+
};
|
|
5636
|
+
microRootDocument.prototype.getElementsByName = function getElementsByName(key) {
|
|
5637
|
+
const _this = getDefaultRawTarget(this);
|
|
5638
|
+
if (isInvalidQuerySelectorKey(key)) {
|
|
5639
|
+
return globalEnv.rawGetElementsByName.call(_this, key);
|
|
5640
|
+
}
|
|
5641
|
+
try {
|
|
5642
|
+
return querySelectorAll.call(this, `[name=${key}]`);
|
|
5643
|
+
}
|
|
5644
|
+
catch (_a) {
|
|
5645
|
+
return globalEnv.rawGetElementsByName.call(_this, key);
|
|
5646
|
+
}
|
|
5647
|
+
};
|
|
5648
|
+
}
|
|
5649
|
+
function patchDocumentProperties(appName, microAppWindow, proxyLocation) {
|
|
5650
|
+
const rawDocument = globalEnv.rawDocument;
|
|
5651
|
+
const microRootDocument = microAppWindow.Document;
|
|
5652
|
+
const microDocument = microAppWindow.document;
|
|
5653
|
+
const getCommonDescriptor = (key, getter) => {
|
|
5654
|
+
const { enumerable } = Object.getOwnPropertyDescriptor(microRootDocument.prototype, key) || {
|
|
5655
|
+
enumerable: true,
|
|
5656
|
+
writable: true,
|
|
5657
|
+
};
|
|
5658
|
+
return {
|
|
5659
|
+
configurable: true,
|
|
5660
|
+
enumerable,
|
|
5661
|
+
get: getter,
|
|
5662
|
+
};
|
|
5663
|
+
};
|
|
5664
|
+
const createDescriptors = () => {
|
|
5665
|
+
const result = {};
|
|
5666
|
+
const descList = [
|
|
5667
|
+
['documentURI', () => proxyLocation.href],
|
|
5668
|
+
['URL', () => proxyLocation.href],
|
|
5669
|
+
['documentElement', () => rawDocument.documentElement],
|
|
5670
|
+
['scrollingElement', () => rawDocument.scrollingElement],
|
|
5671
|
+
['forms', () => microRootDocument.prototype.querySelectorAll.call(microDocument, 'form')],
|
|
5672
|
+
['images', () => microRootDocument.prototype.querySelectorAll.call(microDocument, 'img')],
|
|
5673
|
+
['links', () => microRootDocument.prototype.querySelectorAll.call(microDocument, 'a')],
|
|
5674
|
+
];
|
|
5675
|
+
descList.forEach((desc) => {
|
|
5676
|
+
result[desc[0]] = getCommonDescriptor(desc[0], desc[1]);
|
|
5677
|
+
});
|
|
5678
|
+
// TODO: shadowDOM
|
|
5679
|
+
proxy2RawDocOrShadowKeys.forEach((key) => {
|
|
5680
|
+
result[key] = getCommonDescriptor(key, () => rawDocument[key]);
|
|
5681
|
+
});
|
|
5682
|
+
// TODO: shadowDOM
|
|
5683
|
+
proxy2RawDocOrShadowMethods.forEach((key) => {
|
|
5684
|
+
result[key] = getCommonDescriptor(key, () => bindFunctionToRawTarget(rawDocument[key], rawDocument, 'DOCUMENT'));
|
|
5685
|
+
});
|
|
5686
|
+
proxy2RawDocumentKeys.forEach((key) => {
|
|
5687
|
+
result[key] = getCommonDescriptor(key, () => rawDocument[key]);
|
|
5688
|
+
});
|
|
5689
|
+
proxy2RawDocumentMethods.forEach((key) => {
|
|
5690
|
+
result[key] = getCommonDescriptor(key, () => bindFunctionToRawTarget(rawDocument[key], rawDocument, 'DOCUMENT'));
|
|
5691
|
+
});
|
|
5692
|
+
return result;
|
|
5693
|
+
};
|
|
5694
|
+
rawDefineProperties(microRootDocument.prototype, createDescriptors());
|
|
5695
|
+
// head, body, html, title
|
|
5696
|
+
uniqueDocumentElement.forEach((tagName) => {
|
|
5697
|
+
rawDefineProperty(microDocument, tagName, {
|
|
5698
|
+
enumerable: true,
|
|
5699
|
+
configurable: true,
|
|
5700
|
+
get: () => rawDocument[tagName],
|
|
5701
|
+
set: undefined,
|
|
5702
|
+
});
|
|
5703
|
+
});
|
|
5704
|
+
}
|
|
5705
|
+
function documentEffect(appName, microAppWindow) {
|
|
5706
|
+
const documentEventListenerMap = new Map();
|
|
5707
|
+
const sstDocumentListenerMap = new Map();
|
|
5708
|
+
let onClickHandler = null;
|
|
5709
|
+
let sstOnClickHandler = null;
|
|
5710
|
+
const microRootDocument = microAppWindow.Document;
|
|
5711
|
+
const microDocument = microAppWindow.document;
|
|
5712
|
+
const { rawDocument, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
|
|
5713
|
+
function getEventTarget(type, bindTarget) {
|
|
5714
|
+
return scopeIframeDocumentEvent.includes(type) ? bindTarget : rawDocument;
|
|
5715
|
+
}
|
|
5716
|
+
microRootDocument.prototype.addEventListener = function (type, listener, options) {
|
|
5717
|
+
const handler = isFunction(listener) ? (listener.__MICRO_APP_BOUND_FUNCTION__ = listener.__MICRO_APP_BOUND_FUNCTION__ || listener.bind(this)) : listener;
|
|
5718
|
+
const appListenersMap = documentEventListenerMap.get(appName);
|
|
5719
|
+
if (appListenersMap) {
|
|
5720
|
+
const appListenerList = appListenersMap.get(type);
|
|
5721
|
+
if (appListenerList) {
|
|
5722
|
+
appListenerList.add(listener);
|
|
5723
|
+
}
|
|
5724
|
+
else {
|
|
5725
|
+
appListenersMap.set(type, new Set([listener]));
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
else {
|
|
5729
|
+
documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
|
|
5730
|
+
}
|
|
5731
|
+
listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
|
|
5732
|
+
rawAddEventListener.call(getEventTarget(type, this), type, handler, options);
|
|
5733
|
+
};
|
|
5734
|
+
microRootDocument.prototype.removeEventListener = function (type, listener, options) {
|
|
5735
|
+
const appListenersMap = documentEventListenerMap.get(appName);
|
|
5736
|
+
if (appListenersMap) {
|
|
5737
|
+
const appListenerList = appListenersMap.get(type);
|
|
5738
|
+
if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
|
|
5739
|
+
appListenerList.delete(listener);
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5742
|
+
const handler = (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener;
|
|
5743
|
+
rawRemoveEventListener.call(getEventTarget(type, this), type, handler, options);
|
|
5744
|
+
};
|
|
5745
|
+
// 重新定义microRootDocument.prototype 上的on开头方法
|
|
5746
|
+
function createSetterHandler(eventName) {
|
|
5747
|
+
if (eventName === 'onclick') {
|
|
5748
|
+
return (value) => {
|
|
5749
|
+
if (isFunction(onClickHandler)) {
|
|
5750
|
+
rawRemoveEventListener.call(rawDocument, 'click', onClickHandler, false);
|
|
5751
|
+
}
|
|
5752
|
+
if (isFunction(value)) {
|
|
5753
|
+
onClickHandler = value.bind(microDocument);
|
|
5754
|
+
rawAddEventListener.call(rawDocument, 'click', onClickHandler, false);
|
|
5755
|
+
}
|
|
5756
|
+
else {
|
|
5757
|
+
onClickHandler = value;
|
|
5758
|
+
}
|
|
5759
|
+
};
|
|
5760
|
+
}
|
|
5761
|
+
return (value) => { rawDocument[eventName] = isFunction(value) ? value.bind(microDocument) : value; };
|
|
5762
|
+
}
|
|
5763
|
+
/**
|
|
5764
|
+
* TODO:
|
|
5765
|
+
* 1、直接代理到原生document是否正确
|
|
5766
|
+
* 2、shadowDOM
|
|
5767
|
+
*/
|
|
5768
|
+
Object.getOwnPropertyNames(microRootDocument.prototype)
|
|
5769
|
+
.filter((key) => /^on/.test(key) && !scopeIframeDocumentOnEvent.includes(key))
|
|
5770
|
+
.forEach((eventName) => {
|
|
5771
|
+
const { enumerable, writable, set } = Object.getOwnPropertyDescriptor(microRootDocument.prototype, eventName) || {
|
|
5772
|
+
enumerable: true,
|
|
5773
|
+
writable: true,
|
|
5774
|
+
};
|
|
5775
|
+
try {
|
|
5776
|
+
rawDefineProperty(microRootDocument.prototype, eventName, {
|
|
5777
|
+
enumerable,
|
|
5778
|
+
configurable: true,
|
|
5779
|
+
get: () => {
|
|
5780
|
+
if (eventName === 'onclick')
|
|
5781
|
+
return onClickHandler;
|
|
5782
|
+
return rawDocument[eventName];
|
|
5783
|
+
},
|
|
5784
|
+
set: (writable !== null && writable !== void 0 ? writable : !!set) ? createSetterHandler(eventName) : undefined,
|
|
5785
|
+
});
|
|
5786
|
+
}
|
|
5787
|
+
catch (e) {
|
|
5788
|
+
logWarn(e, appName);
|
|
5789
|
+
}
|
|
5790
|
+
});
|
|
5791
|
+
const reset = () => {
|
|
5792
|
+
sstDocumentListenerMap.clear();
|
|
5793
|
+
sstOnClickHandler = null;
|
|
5794
|
+
};
|
|
5795
|
+
/**
|
|
5796
|
+
* record event
|
|
5797
|
+
* NOTE:
|
|
5798
|
+
* 1.record maybe call twice when unmount prerender, keep-alive app manually with umd mode
|
|
5799
|
+
* Scenes:
|
|
5800
|
+
* 1. exec umdMountHook in umd mode
|
|
5801
|
+
* 2. hidden keep-alive app
|
|
5802
|
+
* 3. after init prerender app
|
|
5803
|
+
*/
|
|
5804
|
+
const record = () => {
|
|
5805
|
+
// record onclick handler
|
|
5806
|
+
sstOnClickHandler = sstOnClickHandler || onClickHandler;
|
|
5807
|
+
// record document event
|
|
5808
|
+
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
5809
|
+
if (documentAppListenersMap) {
|
|
5810
|
+
documentAppListenersMap.forEach((listenerList, type) => {
|
|
5811
|
+
if (listenerList.size) {
|
|
5812
|
+
sstDocumentListenerMap.set(type, new Set(listenerList));
|
|
5813
|
+
}
|
|
5814
|
+
});
|
|
5815
|
+
}
|
|
5816
|
+
};
|
|
5817
|
+
// rebuild event and timer before remount app
|
|
5818
|
+
const rebuild = () => {
|
|
5819
|
+
// rebuild onclick event
|
|
5820
|
+
if (sstOnClickHandler)
|
|
5821
|
+
microDocument.onclick = sstOnClickHandler;
|
|
5822
|
+
sstDocumentListenerMap.forEach((listenerList, type) => {
|
|
5823
|
+
for (const listener of listenerList) {
|
|
5824
|
+
microDocument.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
|
|
5825
|
+
}
|
|
5826
|
+
});
|
|
5827
|
+
reset();
|
|
5828
|
+
};
|
|
5829
|
+
const release = () => {
|
|
5830
|
+
// Clear the function bound by micro application through document.onclick
|
|
5831
|
+
if (isFunction(onClickHandler)) {
|
|
5832
|
+
rawRemoveEventListener.call(rawDocument, 'click', onClickHandler);
|
|
5833
|
+
onClickHandler = null;
|
|
5834
|
+
}
|
|
5835
|
+
// Clear document binding event
|
|
5836
|
+
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
5837
|
+
if (documentAppListenersMap) {
|
|
5838
|
+
documentAppListenersMap.forEach((listenerList, type) => {
|
|
5839
|
+
for (const listener of listenerList) {
|
|
5840
|
+
rawRemoveEventListener.call(getEventTarget(type, microDocument), type, (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener);
|
|
5841
|
+
}
|
|
5842
|
+
});
|
|
5843
|
+
documentAppListenersMap.clear();
|
|
5844
|
+
}
|
|
5845
|
+
};
|
|
5846
|
+
return {
|
|
5847
|
+
reset,
|
|
5848
|
+
record,
|
|
5849
|
+
rebuild,
|
|
5850
|
+
release,
|
|
5851
|
+
};
|
|
5852
|
+
}
|
|
5853
|
+
|
|
5854
|
+
function patchIframeElement(appName, url, microAppWindow, iframeSandbox) {
|
|
5855
|
+
patchIframeNode(appName, microAppWindow, iframeSandbox);
|
|
5856
|
+
patchIframeAttribute(appName, url, microAppWindow);
|
|
5857
|
+
}
|
|
5858
|
+
function patchIframeNode(appName, microAppWindow, iframeSandbox) {
|
|
5859
|
+
const microDocument = microAppWindow.document;
|
|
5860
|
+
const rawDocument = globalEnv.rawDocument;
|
|
5861
|
+
const microRootNode = microAppWindow.Node;
|
|
5862
|
+
const microRootElement = microAppWindow.Element;
|
|
5863
|
+
// const rawMicroGetRootNode = microRootNode.prototype.getRootNode
|
|
5864
|
+
const rawMicroAppendChild = microRootNode.prototype.appendChild;
|
|
5865
|
+
const rawMicroInsertBefore = microRootNode.prototype.insertBefore;
|
|
5866
|
+
const rawMicroReplaceChild = microRootNode.prototype.replaceChild;
|
|
5867
|
+
const rawMicroCloneNode = microRootNode.prototype.cloneNode;
|
|
5868
|
+
const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(microRootElement.prototype, 'innerHTML');
|
|
5869
|
+
const rawParentNodeLDesc = Object.getOwnPropertyDescriptor(microRootNode.prototype, 'parentNode');
|
|
5870
|
+
const isPureNode = (target) => {
|
|
5871
|
+
return (isScriptElement(target) || isBaseElement(target)) && target.__PURE_ELEMENT__;
|
|
5872
|
+
};
|
|
5873
|
+
const getRawTarget = (target) => {
|
|
5874
|
+
if (target === iframeSandbox.microHead) {
|
|
5875
|
+
return rawDocument.head;
|
|
5876
|
+
}
|
|
5877
|
+
else if (target === iframeSandbox.microBody) {
|
|
5878
|
+
return rawDocument.body;
|
|
5879
|
+
}
|
|
5880
|
+
return target;
|
|
5881
|
+
};
|
|
5882
|
+
microRootNode.prototype.getRootNode = function getRootNode() {
|
|
5883
|
+
return microDocument;
|
|
5884
|
+
// TODO: 什么情况下返回原生document?
|
|
5885
|
+
// const rootNode = rawMicroGetRootNode.call(this, options)
|
|
5886
|
+
// if (rootNode === appInstanceMap.get(appName)?.container) return microDocument
|
|
5887
|
+
// return rootNode
|
|
5888
|
+
};
|
|
5889
|
+
microRootNode.prototype.appendChild = function appendChild(node) {
|
|
5890
|
+
updateElementInfo(node, appName);
|
|
5891
|
+
// TODO:只有script才可以这样拦截,link、style不应该拦截
|
|
5892
|
+
if (isPureNode(node)) {
|
|
5893
|
+
return rawMicroAppendChild.call(this, node);
|
|
5894
|
+
}
|
|
5895
|
+
const _this = getRawTarget(this);
|
|
5896
|
+
if (_this !== this) {
|
|
5897
|
+
return _this.appendChild(node);
|
|
5898
|
+
}
|
|
5899
|
+
return rawMicroAppendChild.call(_this, node);
|
|
5900
|
+
};
|
|
5901
|
+
// TODO: 更多场景适配
|
|
5902
|
+
microRootNode.prototype.insertBefore = function insertBefore(node, child) {
|
|
5903
|
+
updateElementInfo(node, appName);
|
|
5904
|
+
if (isPureNode(node)) {
|
|
5905
|
+
return rawMicroInsertBefore.call(this, node, child);
|
|
5906
|
+
}
|
|
5907
|
+
const _this = getRawTarget(this);
|
|
5908
|
+
if (_this !== this) {
|
|
5909
|
+
if (child && !_this.contains(child)) {
|
|
5910
|
+
return _this.appendChild(node);
|
|
5911
|
+
}
|
|
5912
|
+
return _this.insertBefore(node, child);
|
|
5913
|
+
}
|
|
5914
|
+
return rawMicroInsertBefore.call(_this, node, child);
|
|
5915
|
+
};
|
|
5916
|
+
// TODO: 更多场景适配
|
|
5917
|
+
microRootNode.prototype.replaceChild = function replaceChild(node, child) {
|
|
5918
|
+
updateElementInfo(node, appName);
|
|
5919
|
+
if (isPureNode(node)) {
|
|
5920
|
+
return rawMicroReplaceChild.call(this, node, child);
|
|
5921
|
+
}
|
|
5922
|
+
const _this = getRawTarget(this);
|
|
5923
|
+
if (_this !== this) {
|
|
5924
|
+
if (child && !_this.contains(child)) {
|
|
5925
|
+
_this.appendChild(node);
|
|
5926
|
+
return child;
|
|
5927
|
+
}
|
|
5928
|
+
return _this.replaceChild(node, child);
|
|
5929
|
+
}
|
|
5930
|
+
return rawMicroReplaceChild.call(_this, node, child);
|
|
5931
|
+
};
|
|
5932
|
+
// patch cloneNode
|
|
5933
|
+
microRootNode.prototype.cloneNode = function cloneNode(deep) {
|
|
5934
|
+
const clonedNode = rawMicroCloneNode.call(this, deep);
|
|
5935
|
+
return updateElementInfo(clonedNode, appName);
|
|
5936
|
+
};
|
|
5937
|
+
rawDefineProperty(microRootElement.prototype, 'innerHTML', {
|
|
5938
|
+
configurable: true,
|
|
5939
|
+
enumerable: true,
|
|
5940
|
+
get() {
|
|
5941
|
+
return rawInnerHTMLDesc.get.call(this);
|
|
5942
|
+
},
|
|
5943
|
+
set(code) {
|
|
5944
|
+
rawInnerHTMLDesc.set.call(this, code);
|
|
5945
|
+
Array.from(this.children).forEach((child) => {
|
|
5946
|
+
if (isElement(child)) {
|
|
5947
|
+
updateElementInfo(child, appName);
|
|
5948
|
+
}
|
|
5949
|
+
});
|
|
5950
|
+
}
|
|
5951
|
+
});
|
|
5952
|
+
// patch parentNode
|
|
5953
|
+
rawDefineProperty(microRootNode.prototype, 'parentNode', {
|
|
5954
|
+
configurable: true,
|
|
5955
|
+
enumerable: true,
|
|
5956
|
+
get() {
|
|
5957
|
+
var _a;
|
|
5958
|
+
// set html.parentNode to microDocument
|
|
5959
|
+
throttleDeferForParentNode(microDocument);
|
|
5960
|
+
const result = rawParentNodeLDesc.get.call(this);
|
|
5961
|
+
/**
|
|
5962
|
+
* If parentNode is <micro-app-body>, return rawDocument.body
|
|
5963
|
+
* Scenes:
|
|
5964
|
+
* 1. element-ui@2/lib/utils/vue-popper.js
|
|
5965
|
+
* if (this.popperElm.parentNode === document.body) ...
|
|
5966
|
+
* WARNING:
|
|
5967
|
+
* Will it cause other problems ?
|
|
5968
|
+
* e.g. target.parentNode.remove(target)
|
|
5969
|
+
*/
|
|
5970
|
+
if ((result === null || result === void 0 ? void 0 : result.tagName) === 'MICRO-APP-BODY' && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
|
|
5971
|
+
return rawDocument.body;
|
|
5972
|
+
}
|
|
5973
|
+
return result;
|
|
5974
|
+
},
|
|
5975
|
+
set: undefined,
|
|
5976
|
+
});
|
|
5977
|
+
// Adapt to new image(...) scene
|
|
5978
|
+
const ImageProxy = new Proxy(microAppWindow.Image, {
|
|
5979
|
+
construct(Target, args) {
|
|
5980
|
+
const elementImage = new Target(...args);
|
|
5981
|
+
updateElementInfo(elementImage, appName);
|
|
5982
|
+
return elementImage;
|
|
5983
|
+
},
|
|
5984
|
+
});
|
|
5985
|
+
rawDefineProperty(microAppWindow, 'Image', {
|
|
5986
|
+
configurable: true,
|
|
5987
|
+
writable: true,
|
|
5988
|
+
value: ImageProxy,
|
|
5989
|
+
});
|
|
5990
|
+
/**
|
|
5991
|
+
* TODO:
|
|
5992
|
+
* 1、append prepend
|
|
5993
|
+
* 2、cloneNode -- 完成
|
|
5994
|
+
* 3、innerHTML
|
|
5995
|
+
* 4、querySelector、querySelectorAll (head, body)
|
|
5996
|
+
* 5、Image -- 完成
|
|
5997
|
+
* 都是Element原型链上的方法
|
|
5998
|
+
*/
|
|
5999
|
+
}
|
|
6000
|
+
function patchIframeAttribute(appName, url, microAppWindow) {
|
|
6001
|
+
const microRootElement = microAppWindow.Element;
|
|
6002
|
+
const rawMicroSetAttribute = microRootElement.prototype.setAttribute;
|
|
6003
|
+
microRootElement.prototype.setAttribute = function setAttribute(key, value) {
|
|
6004
|
+
if (((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
|
|
6005
|
+
(key === 'href' && /^link$/i.test(this.tagName))) {
|
|
6006
|
+
value = CompletionPath(value, url);
|
|
6007
|
+
}
|
|
6008
|
+
rawMicroSetAttribute.call(this, key, value);
|
|
6009
|
+
};
|
|
6010
|
+
const protoAttrList = [
|
|
6011
|
+
[microAppWindow.HTMLImageElement.prototype, 'src'],
|
|
6012
|
+
[microAppWindow.HTMLScriptElement.prototype, 'src'],
|
|
6013
|
+
[microAppWindow.HTMLLinkElement.prototype, 'href'],
|
|
6014
|
+
];
|
|
6015
|
+
protoAttrList.forEach(([target, attr]) => {
|
|
6016
|
+
const { enumerable, configurable, get, set } = Object.getOwnPropertyDescriptor(target, attr) || {
|
|
6017
|
+
enumerable: true,
|
|
6018
|
+
configurable: true,
|
|
6019
|
+
};
|
|
6020
|
+
rawDefineProperty(target, attr, {
|
|
6021
|
+
enumerable,
|
|
6022
|
+
configurable,
|
|
6023
|
+
get: function () {
|
|
6024
|
+
return get === null || get === void 0 ? void 0 : get.call(this);
|
|
6025
|
+
},
|
|
6026
|
+
set: function (value) {
|
|
6027
|
+
set === null || set === void 0 ? void 0 : set.call(this, CompletionPath(value, url));
|
|
6028
|
+
},
|
|
6029
|
+
});
|
|
6030
|
+
});
|
|
6031
|
+
}
|
|
6032
|
+
|
|
6033
|
+
class IframeSandbox {
|
|
6034
|
+
constructor(appName, url) {
|
|
6035
|
+
this.active = false;
|
|
6036
|
+
// Properties that can be escape to rawWindow
|
|
6037
|
+
this.escapeProperties = [];
|
|
6038
|
+
// Properties escape to rawWindow, cleared when unmount
|
|
6039
|
+
this.escapeKeys = new Set();
|
|
6040
|
+
// TODO: 初始化和每次跳转时都要更新base的href
|
|
6041
|
+
this.updateIframeBase = () => {
|
|
6042
|
+
var _a;
|
|
6043
|
+
(_a = this.baseElement) === null || _a === void 0 ? void 0 : _a.setAttribute('href', this.proxyLocation.protocol + '//' + this.proxyLocation.host + this.proxyLocation.pathname);
|
|
6044
|
+
};
|
|
6045
|
+
const rawLocation = globalEnv.rawWindow.location;
|
|
6046
|
+
const browserHost = rawLocation.protocol + '//' + rawLocation.host;
|
|
6047
|
+
const childStaticLocation = new URL(url);
|
|
6048
|
+
const childHost = childStaticLocation.protocol + '//' + childStaticLocation.host;
|
|
6049
|
+
const childFullPath = childStaticLocation.pathname + childStaticLocation.search + childStaticLocation.hash;
|
|
6050
|
+
this.deleteIframeElement = this.createIframeElement(appName, browserHost);
|
|
6051
|
+
this.microAppWindow = this.iframe.contentWindow;
|
|
6052
|
+
// TODO: 优化代码
|
|
6053
|
+
// exec before initStaticGlobalKeys
|
|
6054
|
+
this.createProxyLocation(appName, url, this.microAppWindow, childStaticLocation, browserHost, childHost);
|
|
6055
|
+
this.createProxyWindow(appName, this.microAppWindow);
|
|
6056
|
+
this.initStaticGlobalKeys(appName, url);
|
|
6057
|
+
// get escapeProperties from plugins
|
|
6058
|
+
this.getSpecialProperties(appName);
|
|
6059
|
+
this.patchIframe(this.microAppWindow, (resolve) => {
|
|
6060
|
+
this.createIframeTemplate(this.microAppWindow);
|
|
6061
|
+
patchIframeRoute(appName, this.microAppWindow, childFullPath);
|
|
6062
|
+
this.windowEffect = patchIframeWindow(appName, this.microAppWindow);
|
|
6063
|
+
this.documentEffect = patchIframeDocument(appName, this.microAppWindow, this.proxyLocation);
|
|
6064
|
+
patchIframeElement(appName, url, this.microAppWindow, this);
|
|
6065
|
+
resolve();
|
|
6066
|
+
});
|
|
6067
|
+
}
|
|
6068
|
+
/**
|
|
6069
|
+
* create iframe for sandbox
|
|
6070
|
+
* @param appName app name
|
|
6071
|
+
* @param browserHost browser origin
|
|
6072
|
+
* @returns release callback
|
|
6073
|
+
*/
|
|
6074
|
+
createIframeElement(appName, browserHost) {
|
|
6075
|
+
this.iframe = pureCreateElement('iframe');
|
|
6076
|
+
const iframeAttrs = {
|
|
6077
|
+
src: browserHost,
|
|
6078
|
+
style: 'display: none',
|
|
6079
|
+
id: appName,
|
|
6080
|
+
};
|
|
6081
|
+
Object.keys(iframeAttrs).forEach((key) => this.iframe.setAttribute(key, iframeAttrs[key]));
|
|
6082
|
+
// effect action during construct
|
|
6083
|
+
globalEnv.rawDocument.body.appendChild(this.iframe);
|
|
6084
|
+
/**
|
|
6085
|
+
* If dom operated async when unmount, premature deletion of iframe will cause unexpected problems
|
|
6086
|
+
* e.g.
|
|
6087
|
+
* 1. antd: notification.destroy()
|
|
6088
|
+
* WARNING:
|
|
6089
|
+
* If async operation time is too long, defer cannot avoid the problem
|
|
6090
|
+
* TODO: more test
|
|
6091
|
+
*/
|
|
6092
|
+
return () => defer(() => {
|
|
6093
|
+
var _a, _b;
|
|
6094
|
+
// default mode or destroy, iframe will be deleted when unmount
|
|
6095
|
+
(_b = (_a = this.iframe) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.iframe);
|
|
6096
|
+
this.iframe = null;
|
|
6097
|
+
});
|
|
6098
|
+
}
|
|
6099
|
+
start({ baseroute, useMemoryRouter, defaultPage, disablePatchRequest, }) {
|
|
6100
|
+
if (!this.active) {
|
|
6101
|
+
this.active = true;
|
|
6102
|
+
// TODO: 虚拟路由升级
|
|
6103
|
+
if (useMemoryRouter) {
|
|
6104
|
+
this.initRouteState(defaultPage);
|
|
6105
|
+
// unique listener of popstate event for sub app
|
|
6106
|
+
this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
|
|
6107
|
+
}
|
|
6108
|
+
else {
|
|
6109
|
+
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
|
|
6110
|
+
}
|
|
6111
|
+
/**
|
|
6112
|
+
* create base element to iframe
|
|
6113
|
+
* WARNING: This will also affect a, image, link and script
|
|
6114
|
+
*/
|
|
6115
|
+
if (!disablePatchRequest) {
|
|
6116
|
+
this.createIframeBase();
|
|
6117
|
+
}
|
|
6118
|
+
if (++globalEnv.activeSandbox === 1) {
|
|
6119
|
+
patchElementAndDocument();
|
|
6120
|
+
patchHistory();
|
|
6121
|
+
}
|
|
6122
|
+
if (++IframeSandbox.activeCount === 1) ;
|
|
6123
|
+
}
|
|
6124
|
+
}
|
|
6125
|
+
stop({ umdMode, keepRouteState, destroy, clearData, }) {
|
|
6126
|
+
if (this.active) {
|
|
6127
|
+
this.recordAndReleaseEffect({ clearData }, !umdMode || destroy);
|
|
6128
|
+
if (this.removeHistoryListener) {
|
|
6129
|
+
this.clearRouteState(keepRouteState);
|
|
6130
|
+
// release listener of popstate
|
|
6131
|
+
this.removeHistoryListener();
|
|
6132
|
+
}
|
|
6133
|
+
if (!umdMode || destroy) {
|
|
6134
|
+
this.deleteIframeElement();
|
|
6135
|
+
this.escapeKeys.forEach((key) => {
|
|
6136
|
+
Reflect.deleteProperty(globalEnv.rawWindow, key);
|
|
6137
|
+
});
|
|
6138
|
+
this.escapeKeys.clear();
|
|
6139
|
+
}
|
|
6140
|
+
if (--globalEnv.activeSandbox === 0) {
|
|
6141
|
+
releasePatchElementAndDocument();
|
|
6142
|
+
releasePatchHistory();
|
|
6143
|
+
}
|
|
6144
|
+
if (--IframeSandbox.activeCount === 0) ;
|
|
6145
|
+
this.active = false;
|
|
6146
|
+
}
|
|
6147
|
+
}
|
|
6148
|
+
/**
|
|
6149
|
+
* Record global effect and then release (effect: global event, timeout, data listener)
|
|
6150
|
+
* Scenes:
|
|
6151
|
+
* 1. unmount of default/umd app
|
|
6152
|
+
* 2. hidden keep-alive app
|
|
6153
|
+
* 3. after init prerender app
|
|
6154
|
+
* @param options {
|
|
6155
|
+
* @param clearData clear data from base app
|
|
6156
|
+
* @param isPrerender is prerender app
|
|
6157
|
+
* @param keepAlive is keep-alive app
|
|
6158
|
+
* }
|
|
6159
|
+
* @param preventRecord prevent record effect events (default or destroy)
|
|
6160
|
+
*/
|
|
6161
|
+
recordAndReleaseEffect(options, preventRecord = false) {
|
|
6162
|
+
if (preventRecord) {
|
|
6163
|
+
this.resetEffectSnapshot();
|
|
6164
|
+
}
|
|
6165
|
+
else {
|
|
6166
|
+
this.recordEffectSnapshot();
|
|
6167
|
+
}
|
|
6168
|
+
this.releaseGlobalEffect(options);
|
|
6169
|
+
}
|
|
6170
|
+
/**
|
|
6171
|
+
* reset effect snapshot data in default mode or destroy
|
|
6172
|
+
* Scenes:
|
|
6173
|
+
* 1. unmount hidden keep-alive app manually
|
|
6174
|
+
* 2. unmount prerender app manually
|
|
6175
|
+
*/
|
|
6176
|
+
resetEffectSnapshot() {
|
|
6177
|
+
this.windowEffect.reset();
|
|
6178
|
+
this.documentEffect.reset();
|
|
6179
|
+
resetDataCenterSnapshot(this.microAppWindow.microApp);
|
|
6180
|
+
}
|
|
6181
|
+
/**
|
|
6182
|
+
* record umd snapshot before the first execution of umdHookMount
|
|
6183
|
+
* Scenes:
|
|
6184
|
+
* 1. exec umdMountHook in umd mode
|
|
6185
|
+
* 2. hidden keep-alive app
|
|
6186
|
+
* 3. after init prerender app
|
|
6187
|
+
*/
|
|
6188
|
+
recordEffectSnapshot() {
|
|
6189
|
+
this.windowEffect.record();
|
|
6190
|
+
this.documentEffect.record();
|
|
6191
|
+
recordDataCenterSnapshot(this.microAppWindow.microApp);
|
|
6192
|
+
}
|
|
6193
|
+
// rebuild umd snapshot before remount umd app
|
|
6194
|
+
rebuildEffectSnapshot() {
|
|
6195
|
+
this.windowEffect.rebuild();
|
|
6196
|
+
this.documentEffect.rebuild();
|
|
6197
|
+
rebuildDataCenterSnapshot(this.microAppWindow.microApp);
|
|
6198
|
+
}
|
|
6199
|
+
/**
|
|
6200
|
+
* clear global event, timeout, data listener
|
|
6201
|
+
* Scenes:
|
|
6202
|
+
* 1. unmount of normal/umd app
|
|
6203
|
+
* 2. hidden keep-alive app
|
|
6204
|
+
* 3. after init prerender app
|
|
6205
|
+
* @param clearData clear data from base app
|
|
6206
|
+
* @param isPrerender is prerender app
|
|
6207
|
+
* @param keepAlive is keep-alive app
|
|
6208
|
+
*/
|
|
6209
|
+
releaseGlobalEffect({ clearData = false }) {
|
|
6210
|
+
var _a, _b, _c;
|
|
6211
|
+
this.windowEffect.release();
|
|
6212
|
+
this.documentEffect.release();
|
|
6213
|
+
(_a = this.microAppWindow.microApp) === null || _a === void 0 ? void 0 : _a.clearDataListener();
|
|
6214
|
+
(_b = this.microAppWindow.microApp) === null || _b === void 0 ? void 0 : _b.clearGlobalDataListener();
|
|
6215
|
+
if (clearData) {
|
|
6216
|
+
microApp.clearData(this.microAppWindow.__MICRO_APP_NAME__);
|
|
6217
|
+
(_c = this.microAppWindow.microApp) === null || _c === void 0 ? void 0 : _c.clearData();
|
|
6218
|
+
}
|
|
6219
|
+
}
|
|
6220
|
+
// set __MICRO_APP_PRE_RENDER__ state
|
|
6221
|
+
setPreRenderState(state) {
|
|
6222
|
+
this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
|
|
4875
6223
|
}
|
|
4876
|
-
|
|
4877
|
-
|
|
6224
|
+
markUmdMode(state) {
|
|
6225
|
+
this.microAppWindow.__MICRO_APP_UMD_MODE__ = state;
|
|
6226
|
+
}
|
|
6227
|
+
initStaticGlobalKeys(appName, url) {
|
|
6228
|
+
this.microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
|
|
6229
|
+
this.microAppWindow.__MICRO_APP_NAME__ = appName;
|
|
6230
|
+
this.microAppWindow.__MICRO_APP_URL__ = url;
|
|
6231
|
+
this.microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
|
|
6232
|
+
this.microAppWindow.__MICRO_APP_WINDOW__ = this.microAppWindow;
|
|
6233
|
+
this.microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
|
|
6234
|
+
this.microAppWindow.__MICRO_APP_UMD_MODE__ = false;
|
|
6235
|
+
this.microAppWindow.__MICRO_APP_SANDBOX__ = this;
|
|
6236
|
+
this.microAppWindow.__MICRO_APP_PROXY_WINDOW__ = this.proxyWindow;
|
|
6237
|
+
this.microAppWindow.rawWindow = globalEnv.rawWindow;
|
|
6238
|
+
this.microAppWindow.rawDocument = globalEnv.rawDocument;
|
|
6239
|
+
this.microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
|
|
6240
|
+
removeDomScope,
|
|
6241
|
+
pureCreateElement,
|
|
6242
|
+
location: this.proxyLocation,
|
|
6243
|
+
router,
|
|
6244
|
+
});
|
|
4878
6245
|
}
|
|
4879
|
-
|
|
4880
|
-
|
|
6246
|
+
// TODO: RESTRUCTURE
|
|
6247
|
+
patchIframe(microAppWindow, cb) {
|
|
6248
|
+
this.sandboxReady = new Promise((resolve) => {
|
|
6249
|
+
(function iframeLocationReady() {
|
|
6250
|
+
setTimeout(() => {
|
|
6251
|
+
if (microAppWindow.location.href === 'about:blank') {
|
|
6252
|
+
iframeLocationReady();
|
|
6253
|
+
}
|
|
6254
|
+
else {
|
|
6255
|
+
/**
|
|
6256
|
+
* microAppWindow.document rebuild
|
|
6257
|
+
*/
|
|
6258
|
+
microAppWindow.stop();
|
|
6259
|
+
cb(resolve);
|
|
6260
|
+
}
|
|
6261
|
+
}, 0);
|
|
6262
|
+
})();
|
|
6263
|
+
});
|
|
4881
6264
|
}
|
|
4882
|
-
|
|
4883
|
-
|
|
6265
|
+
// TODO: RESTRUCTURE
|
|
6266
|
+
createIframeTemplate(microAppWindow) {
|
|
6267
|
+
const microDocument = microAppWindow.document;
|
|
6268
|
+
clearDOM(microDocument);
|
|
6269
|
+
const html = microDocument.createElement('html');
|
|
6270
|
+
html.innerHTML = '<head></head><body></body>';
|
|
6271
|
+
microDocument.appendChild(html);
|
|
6272
|
+
// 记录iframe原生body
|
|
6273
|
+
this.microBody = microDocument.body;
|
|
6274
|
+
this.microHead = microDocument.head;
|
|
4884
6275
|
}
|
|
4885
6276
|
/**
|
|
4886
|
-
*
|
|
6277
|
+
* baseElement will complete the relative address of element according to the URL
|
|
6278
|
+
* e.g: a image link script fetch ajax EventSource
|
|
4887
6279
|
*/
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
6280
|
+
createIframeBase() {
|
|
6281
|
+
this.baseElement = pureCreateElement('base');
|
|
6282
|
+
this.updateIframeBase();
|
|
6283
|
+
this.microHead.appendChild(this.baseElement);
|
|
6284
|
+
}
|
|
6285
|
+
createProxyLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost) {
|
|
6286
|
+
this.proxyLocation = createMicroLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost);
|
|
6287
|
+
}
|
|
6288
|
+
createProxyWindow(appName, microAppWindow) {
|
|
6289
|
+
const rawWindow = globalEnv.rawWindow;
|
|
6290
|
+
this.proxyWindow = new Proxy(microAppWindow, {
|
|
4897
6291
|
get: (target, key) => {
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
if (key === Symbol.toStringTag)
|
|
4903
|
-
return 'ProxyDocument';
|
|
4904
|
-
if (key === 'defaultView')
|
|
6292
|
+
if (key === 'location') {
|
|
6293
|
+
return this.proxyLocation;
|
|
6294
|
+
}
|
|
6295
|
+
if (globalPropertyList.includes(key.toString())) {
|
|
4905
6296
|
return this.proxyWindow;
|
|
4906
|
-
|
|
4907
|
-
return
|
|
6297
|
+
}
|
|
6298
|
+
return bindFunctionToRawTarget(Reflect.get(target, key), target);
|
|
4908
6299
|
},
|
|
4909
6300
|
set: (target, key, value) => {
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
6301
|
+
if (this.active) {
|
|
6302
|
+
/**
|
|
6303
|
+
* TODO:
|
|
6304
|
+
* 1、location域名相同,子应用内部跳转时的处理
|
|
6305
|
+
* 2、和with沙箱的变量相同,提取成公共数组
|
|
6306
|
+
*/
|
|
6307
|
+
if (key === 'location') {
|
|
6308
|
+
return Reflect.set(rawWindow, key, value);
|
|
6309
|
+
}
|
|
6310
|
+
Reflect.set(target, key, value);
|
|
6311
|
+
if (this.escapeProperties.includes(key)) {
|
|
6312
|
+
!Reflect.has(rawWindow, key) && this.escapeKeys.add(key);
|
|
6313
|
+
Reflect.set(rawWindow, key, value);
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
4915
6316
|
return true;
|
|
4916
|
-
}
|
|
6317
|
+
},
|
|
6318
|
+
has: (target, key) => key in target,
|
|
6319
|
+
deleteProperty: (target, key) => {
|
|
6320
|
+
if (Reflect.has(target, key)) {
|
|
6321
|
+
this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
|
|
6322
|
+
return Reflect.deleteProperty(target, key);
|
|
6323
|
+
}
|
|
6324
|
+
return true;
|
|
6325
|
+
},
|
|
4917
6326
|
});
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
6327
|
+
}
|
|
6328
|
+
/**
|
|
6329
|
+
* get escapeProperties from plugins & adapter
|
|
6330
|
+
* @param appName app name
|
|
6331
|
+
*/
|
|
6332
|
+
getSpecialProperties(appName) {
|
|
6333
|
+
var _a;
|
|
6334
|
+
if (isPlainObject(microApp.options.plugins)) {
|
|
6335
|
+
this.commonActionForSpecialProperties(microApp.options.plugins.global);
|
|
6336
|
+
this.commonActionForSpecialProperties((_a = microApp.options.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
|
|
6337
|
+
}
|
|
6338
|
+
}
|
|
6339
|
+
// common action for global plugins and module plugins
|
|
6340
|
+
commonActionForSpecialProperties(plugins) {
|
|
6341
|
+
if (isArray(plugins)) {
|
|
6342
|
+
for (const plugin of plugins) {
|
|
6343
|
+
if (isPlainObject(plugin)) {
|
|
6344
|
+
if (isArray(plugin.escapeProperties)) {
|
|
6345
|
+
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
4924
6346
|
}
|
|
4925
6347
|
}
|
|
4926
|
-
return (target === proxyDocument ||
|
|
4927
|
-
target instanceof rawRootDocument);
|
|
4928
6348
|
}
|
|
4929
6349
|
}
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
6350
|
+
}
|
|
6351
|
+
initRouteState(defaultPage) {
|
|
6352
|
+
initRouteStateWithURL(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location, defaultPage);
|
|
6353
|
+
}
|
|
6354
|
+
clearRouteState(keepRouteState) {
|
|
6355
|
+
clearRouteStateFromURL(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, this.microAppWindow.location, keepRouteState);
|
|
6356
|
+
}
|
|
6357
|
+
setRouteInfoForKeepAliveApp() {
|
|
6358
|
+
updateBrowserURLWithLocation(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location);
|
|
6359
|
+
}
|
|
6360
|
+
removeRouteInfoForKeepAliveApp() {
|
|
6361
|
+
removeStateAndPathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__);
|
|
6362
|
+
}
|
|
6363
|
+
/**
|
|
6364
|
+
* Format all html elements when init
|
|
6365
|
+
* @param container micro app container
|
|
6366
|
+
*/
|
|
6367
|
+
patchStaticElement(container) {
|
|
6368
|
+
patchElementTree(container, this.microAppWindow.__MICRO_APP_NAME__);
|
|
6369
|
+
}
|
|
6370
|
+
/**
|
|
6371
|
+
* Actions:
|
|
6372
|
+
* 1. patch static elements from html
|
|
6373
|
+
* @param container micro app container
|
|
6374
|
+
*/
|
|
6375
|
+
actionBeforeExecScripts(container) {
|
|
6376
|
+
this.patchStaticElement(container);
|
|
4956
6377
|
}
|
|
4957
6378
|
}
|
|
4958
|
-
|
|
6379
|
+
IframeSandbox.activeCount = 0; // number of active sandbox
|
|
4959
6380
|
|
|
4960
6381
|
function formatEventInfo(event, element) {
|
|
4961
6382
|
Object.defineProperties(event, {
|
|
@@ -5005,38 +6426,40 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
|
|
|
5005
6426
|
}
|
|
5006
6427
|
/**
|
|
5007
6428
|
* Dispatch custom event to micro app
|
|
6429
|
+
* @param app app
|
|
5008
6430
|
* @param eventName event name
|
|
5009
|
-
* @param appName app name
|
|
5010
6431
|
* @param detail event detail
|
|
5011
6432
|
*/
|
|
5012
|
-
function dispatchCustomEventToMicroApp(
|
|
5013
|
-
const event = new CustomEvent(formatEventName
|
|
6433
|
+
function dispatchCustomEventToMicroApp(app, eventName, detail = {}) {
|
|
6434
|
+
const event = new CustomEvent(formatEventName(eventName, app.name), {
|
|
5014
6435
|
detail,
|
|
5015
6436
|
});
|
|
5016
|
-
window
|
|
6437
|
+
const target = app.iframe ? app.sandBox.microAppWindow : window;
|
|
6438
|
+
target.dispatchEvent(event);
|
|
5017
6439
|
}
|
|
5018
6440
|
|
|
5019
6441
|
// micro app instances
|
|
5020
6442
|
const appInstanceMap = new Map();
|
|
5021
6443
|
class CreateApp {
|
|
5022
|
-
constructor({ name, url, container, scopecss, useSandbox, inline,
|
|
6444
|
+
constructor({ name, url, container, scopecss, useSandbox, inline, iframe, ssrUrl, isPrefetch, prefetchLevel, }) {
|
|
5023
6445
|
this.state = appStates.CREATED;
|
|
5024
6446
|
this.keepAliveState = null;
|
|
5025
|
-
this.keepAliveContainer = null;
|
|
5026
6447
|
this.loadSourceLevel = 0;
|
|
5027
6448
|
this.umdHookMount = null;
|
|
5028
6449
|
this.umdHookUnmount = null;
|
|
5029
|
-
this.libraryName = null;
|
|
5030
6450
|
this.umdMode = false;
|
|
6451
|
+
// TODO: 类型优化,加上iframe沙箱
|
|
5031
6452
|
this.sandBox = null;
|
|
5032
6453
|
this.fiber = false;
|
|
5033
6454
|
this.useMemoryRouter = true;
|
|
6455
|
+
appInstanceMap.set(name, this);
|
|
6456
|
+
// init actions
|
|
5034
6457
|
this.name = name;
|
|
5035
6458
|
this.url = url;
|
|
5036
6459
|
this.useSandbox = useSandbox;
|
|
5037
6460
|
this.scopecss = this.useSandbox && scopecss;
|
|
5038
6461
|
this.inline = inline !== null && inline !== void 0 ? inline : false;
|
|
5039
|
-
this.
|
|
6462
|
+
this.iframe = iframe !== null && iframe !== void 0 ? iframe : false;
|
|
5040
6463
|
// not exist when prefetch 👇
|
|
5041
6464
|
this.container = container !== null && container !== void 0 ? container : null;
|
|
5042
6465
|
this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
|
|
@@ -5044,15 +6467,13 @@ class CreateApp {
|
|
|
5044
6467
|
this.isPrefetch = isPrefetch !== null && isPrefetch !== void 0 ? isPrefetch : false;
|
|
5045
6468
|
this.isPrerender = prefetchLevel === 3;
|
|
5046
6469
|
this.prefetchLevel = prefetchLevel;
|
|
5047
|
-
// init actions
|
|
5048
|
-
appInstanceMap.set(this.name, this);
|
|
5049
6470
|
this.source = { html: null, links: new Set(), scripts: new Set() };
|
|
5050
6471
|
this.loadSourceCode();
|
|
5051
|
-
this.
|
|
6472
|
+
this.createSandbox();
|
|
5052
6473
|
}
|
|
5053
6474
|
// Load resources
|
|
5054
6475
|
loadSourceCode() {
|
|
5055
|
-
this.
|
|
6476
|
+
this.setAppState(appStates.LOADING);
|
|
5056
6477
|
HTMLLoader.getInstance().run(this, extractSourceDom);
|
|
5057
6478
|
}
|
|
5058
6479
|
/**
|
|
@@ -5062,7 +6483,7 @@ class CreateApp {
|
|
|
5062
6483
|
var _a;
|
|
5063
6484
|
if (++this.loadSourceLevel === 2) {
|
|
5064
6485
|
this.source.html = html;
|
|
5065
|
-
this.
|
|
6486
|
+
this.setAppState(appStates.LOADED);
|
|
5066
6487
|
if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
|
|
5067
6488
|
getRootContainer(this.container).mount(this);
|
|
5068
6489
|
}
|
|
@@ -5073,7 +6494,7 @@ class CreateApp {
|
|
|
5073
6494
|
* 1. fiber forced on
|
|
5074
6495
|
* 2. only virtual router support
|
|
5075
6496
|
*
|
|
5076
|
-
* NOTE: (
|
|
6497
|
+
* NOTE: (Don't update browser url, dispatch popstateEvent, reload window, dispatch lifecycle event)
|
|
5077
6498
|
* 1. pushState/replaceState in child can update microLocation, but will not attach router info to browser url
|
|
5078
6499
|
* 2. prevent dispatch popstate/hashchange event to browser
|
|
5079
6500
|
* 3. all navigation actions of location are invalid (In the future, we can consider update microLocation without trigger browser reload)
|
|
@@ -5093,7 +6514,6 @@ class CreateApp {
|
|
|
5093
6514
|
useMemoryRouter: true,
|
|
5094
6515
|
baseroute: '',
|
|
5095
6516
|
fiber: true,
|
|
5096
|
-
esmodule: this.esmodule,
|
|
5097
6517
|
defaultPage: defaultPage !== null && defaultPage !== void 0 ? defaultPage : '',
|
|
5098
6518
|
disablePatchRequest: disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : false,
|
|
5099
6519
|
});
|
|
@@ -5108,7 +6528,7 @@ class CreateApp {
|
|
|
5108
6528
|
this.loadSourceLevel = -1;
|
|
5109
6529
|
if (appStates.UNMOUNT !== this.state) {
|
|
5110
6530
|
this.onerror(e);
|
|
5111
|
-
this.
|
|
6531
|
+
this.setAppState(appStates.LOAD_FAILED);
|
|
5112
6532
|
}
|
|
5113
6533
|
}
|
|
5114
6534
|
/**
|
|
@@ -5120,150 +6540,151 @@ class CreateApp {
|
|
|
5120
6540
|
* @param baseroute route prefix, default is ''
|
|
5121
6541
|
* @param disablePatchRequest prevent rewrite request method of child app
|
|
5122
6542
|
* @param fiber run js in fiber mode
|
|
5123
|
-
* @param esmodule support type='module' script
|
|
5124
6543
|
*/
|
|
5125
|
-
mount({ container, inline, useMemoryRouter, defaultPage, baseroute, disablePatchRequest, fiber,
|
|
5126
|
-
var _a, _b, _c, _d, _e, _f;
|
|
6544
|
+
mount({ container, inline, useMemoryRouter, defaultPage, baseroute, disablePatchRequest, fiber, }) {
|
|
5127
6545
|
if (this.loadSourceLevel !== 2) {
|
|
5128
6546
|
/**
|
|
5129
|
-
*
|
|
5130
|
-
*
|
|
5131
|
-
*
|
|
6547
|
+
* container cannot be null when load end
|
|
6548
|
+
* NOTE:
|
|
6549
|
+
* 1. render prefetch app before load end
|
|
6550
|
+
* 2. unmount prefetch app and mount again before load end
|
|
5132
6551
|
*/
|
|
5133
6552
|
this.container = container;
|
|
5134
6553
|
// mount before prerender exec mount (loading source), set isPrerender to false
|
|
5135
6554
|
this.isPrerender = false;
|
|
5136
6555
|
// reset app state to LOADING
|
|
5137
|
-
this.
|
|
6556
|
+
this.setAppState(appStates.LOADING);
|
|
5138
6557
|
return;
|
|
5139
6558
|
}
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
* Transfer the contents of div to the <micro-app> tag
|
|
5144
|
-
*
|
|
5145
|
-
* Special scenes:
|
|
5146
|
-
* 1. mount before prerender exec mount (loading source)
|
|
5147
|
-
* 2. mount when prerender js executing
|
|
5148
|
-
* 3. mount after prerender js exec end
|
|
5149
|
-
*
|
|
5150
|
-
* TODO: test shadowDOM
|
|
5151
|
-
*/
|
|
5152
|
-
if (this.container instanceof HTMLDivElement &&
|
|
5153
|
-
this.container.hasAttribute('prerender')) {
|
|
5154
|
-
/**
|
|
5155
|
-
* rebuild effect event of window, document, data center
|
|
5156
|
-
* explain:
|
|
5157
|
-
* 1. rebuild before exec mount, do nothing
|
|
5158
|
-
* 2. rebuild when js executing, recovery recorded effect event, because prerender fiber mode
|
|
5159
|
-
* 3. rebuild after js exec end, normal recovery effect event
|
|
5160
|
-
*/
|
|
5161
|
-
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
|
|
5162
|
-
// current this.container is <div prerender='true'></div>
|
|
5163
|
-
cloneContainer(this.container, container, false);
|
|
6559
|
+
this.createSandbox();
|
|
6560
|
+
const nextAction = () => {
|
|
6561
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
5164
6562
|
/**
|
|
5165
|
-
*
|
|
5166
|
-
*
|
|
5167
|
-
*
|
|
6563
|
+
* Special scenes:
|
|
6564
|
+
* 1. mount before prerender exec mount (loading source)
|
|
6565
|
+
* 2. mount when prerender js executing
|
|
6566
|
+
* 3. mount after prerender js exec end
|
|
6567
|
+
* 4. mount after prerender unmounted
|
|
6568
|
+
*
|
|
6569
|
+
* TODO: test shadowDOM
|
|
5168
6570
|
*/
|
|
5169
|
-
this.
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
6571
|
+
if (this.isPrerender &&
|
|
6572
|
+
isDivElement(this.container) &&
|
|
6573
|
+
this.container.hasAttribute('prerender')) {
|
|
6574
|
+
/**
|
|
6575
|
+
* rebuild effect event of window, document, data center
|
|
6576
|
+
* explain:
|
|
6577
|
+
* 1. rebuild before exec mount, do nothing
|
|
6578
|
+
* 2. rebuild when js executing, recovery recorded effect event, because prerender fiber mode
|
|
6579
|
+
* 3. rebuild after js exec end, normal recovery effect event
|
|
6580
|
+
*/
|
|
6581
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
|
|
6582
|
+
// current this.container is <div prerender='true'></div>
|
|
6583
|
+
cloneContainer(this.container, container, false);
|
|
6584
|
+
/**
|
|
6585
|
+
* set this.container to <micro-app></micro-app>
|
|
6586
|
+
* NOTE:
|
|
6587
|
+
* must exec before this.preRenderEvents?.forEach((cb) => cb())
|
|
6588
|
+
*/
|
|
6589
|
+
this.container = container;
|
|
6590
|
+
(_b = this.preRenderEvents) === null || _b === void 0 ? void 0 : _b.forEach((cb) => cb());
|
|
6591
|
+
// reset isPrerender config
|
|
6592
|
+
this.isPrerender = false;
|
|
6593
|
+
this.preRenderEvents = null;
|
|
6594
|
+
// attach router info to browser url
|
|
6595
|
+
router.attachToURL(this.name);
|
|
6596
|
+
(_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.setPreRenderState(false);
|
|
6597
|
+
}
|
|
6598
|
+
else {
|
|
6599
|
+
this.container = container;
|
|
6600
|
+
this.inline = inline;
|
|
6601
|
+
this.fiber = fiber;
|
|
6602
|
+
// use in sandbox/effect
|
|
6603
|
+
this.useMemoryRouter = useMemoryRouter;
|
|
6604
|
+
// this.hiddenRouter = hiddenRouter ?? this.hiddenRouter
|
|
6605
|
+
const dispatchBeforeMount = () => dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
|
|
6606
|
+
if (this.isPrerender) {
|
|
6607
|
+
((_d = this.preRenderEvents) !== null && _d !== void 0 ? _d : (this.preRenderEvents = [])).push(dispatchBeforeMount);
|
|
6608
|
+
}
|
|
6609
|
+
else {
|
|
6610
|
+
dispatchBeforeMount();
|
|
6611
|
+
}
|
|
6612
|
+
this.setAppState(appStates.MOUNTING);
|
|
6613
|
+
// TODO: 将所有cloneContainer中的'as Element'去掉,兼容shadowRoot的场景
|
|
6614
|
+
cloneContainer(this.source.html, this.container, !this.umdMode);
|
|
6615
|
+
(_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
|
|
6616
|
+
umdMode: this.umdMode,
|
|
6617
|
+
baseroute,
|
|
6618
|
+
useMemoryRouter,
|
|
6619
|
+
defaultPage,
|
|
6620
|
+
disablePatchRequest,
|
|
6621
|
+
});
|
|
5209
6622
|
if (!this.umdMode) {
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
6623
|
+
// update element info of html
|
|
6624
|
+
(_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.actionBeforeExecScripts(this.container);
|
|
6625
|
+
// if all js are executed, param isFinished will be true
|
|
6626
|
+
execScripts(this, (isFinished) => {
|
|
6627
|
+
if (!this.umdMode) {
|
|
6628
|
+
const { mount, unmount } = this.getUmdLibraryHooks();
|
|
6629
|
+
/**
|
|
6630
|
+
* umdHookUnmount can works in default mode
|
|
6631
|
+
* register through window.unmount
|
|
6632
|
+
*/
|
|
6633
|
+
this.umdHookUnmount = unmount;
|
|
6634
|
+
// if mount & unmount is function, the sub app is umd mode
|
|
6635
|
+
if (isFunction(mount) && isFunction(unmount)) {
|
|
6636
|
+
this.umdHookMount = mount;
|
|
6637
|
+
// sandbox must exist
|
|
6638
|
+
this.sandBox.markUmdMode(this.umdMode = true);
|
|
6639
|
+
try {
|
|
6640
|
+
this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
|
|
6641
|
+
}
|
|
6642
|
+
catch (e) {
|
|
6643
|
+
logError('An error occurred in function mount \n', this.name, e);
|
|
6644
|
+
}
|
|
6645
|
+
}
|
|
6646
|
+
else if (isFinished === true) {
|
|
6647
|
+
this.handleMounted();
|
|
6648
|
+
}
|
|
5228
6649
|
}
|
|
5229
|
-
}
|
|
6650
|
+
});
|
|
5230
6651
|
}
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
((_a = this.preRenderEvent) !== null && _a !== void 0 ? _a : (this.preRenderEvent = [])).push(dispatchMounted);
|
|
5236
|
-
this.recordAndReleaseEffect();
|
|
6652
|
+
else {
|
|
6653
|
+
(_g = this.sandBox) === null || _g === void 0 ? void 0 : _g.rebuildEffectSnapshot();
|
|
6654
|
+
try {
|
|
6655
|
+
this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
|
|
5237
6656
|
}
|
|
5238
|
-
|
|
5239
|
-
|
|
6657
|
+
catch (e) {
|
|
6658
|
+
logError('An error occurred in function mount \n', this.name, e);
|
|
5240
6659
|
}
|
|
5241
6660
|
}
|
|
5242
|
-
});
|
|
5243
|
-
}
|
|
5244
|
-
else {
|
|
5245
|
-
(_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.rebuildEffectSnapshot();
|
|
5246
|
-
try {
|
|
5247
|
-
umdHookMountResult = this.umdHookMount();
|
|
5248
6661
|
}
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
this.handleMounted(umdHookMountResult);
|
|
5253
|
-
}
|
|
6662
|
+
};
|
|
6663
|
+
// TODO: any替换为iframe沙箱类型
|
|
6664
|
+
this.iframe ? this.sandBox.sandboxReady.then(nextAction) : nextAction();
|
|
5254
6665
|
}
|
|
5255
6666
|
/**
|
|
5256
6667
|
* handle for promise umdHookMount
|
|
5257
6668
|
* @param umdHookMountResult result of umdHookMount
|
|
5258
6669
|
*/
|
|
5259
6670
|
handleMounted(umdHookMountResult) {
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
6671
|
+
var _a, _b;
|
|
6672
|
+
const dispatchAction = () => {
|
|
6673
|
+
if (isPromise(umdHookMountResult)) {
|
|
6674
|
+
umdHookMountResult
|
|
6675
|
+
.then(() => this.dispatchMountedEvent())
|
|
6676
|
+
.catch((e) => this.onerror(e));
|
|
6677
|
+
}
|
|
6678
|
+
else {
|
|
6679
|
+
this.dispatchMountedEvent();
|
|
6680
|
+
}
|
|
6681
|
+
};
|
|
6682
|
+
if (this.isPrerender) {
|
|
6683
|
+
(_a = this.preRenderEvents) === null || _a === void 0 ? void 0 : _a.push(dispatchAction);
|
|
6684
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.recordAndReleaseEffect({ isPrerender: true });
|
|
5264
6685
|
}
|
|
5265
6686
|
else {
|
|
5266
|
-
|
|
6687
|
+
dispatchAction();
|
|
5267
6688
|
}
|
|
5268
6689
|
}
|
|
5269
6690
|
/**
|
|
@@ -5271,9 +6692,9 @@ class CreateApp {
|
|
|
5271
6692
|
*/
|
|
5272
6693
|
dispatchMountedEvent() {
|
|
5273
6694
|
if (appStates.UNMOUNT !== this.state) {
|
|
5274
|
-
this.
|
|
6695
|
+
this.setAppState(appStates.MOUNTED);
|
|
5275
6696
|
// call window.onmount of child app
|
|
5276
|
-
|
|
6697
|
+
execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONMOUNT), this.name, microGlobalEvent.ONMOUNT, microApp.getData(this.name, true));
|
|
5277
6698
|
// dispatch event mounted to parent
|
|
5278
6699
|
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
|
|
5279
6700
|
}
|
|
@@ -5287,30 +6708,25 @@ class CreateApp {
|
|
|
5287
6708
|
* @param unmountcb callback of unmount
|
|
5288
6709
|
*/
|
|
5289
6710
|
unmount({ destroy, clearData, keepRouteState, unmountcb, }) {
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
this.state = appStates.UNMOUNT;
|
|
5294
|
-
this.keepAliveState = null;
|
|
5295
|
-
this.keepAliveContainer = null;
|
|
6711
|
+
var _a;
|
|
6712
|
+
destroy = destroy || this.state === appStates.LOAD_FAILED;
|
|
6713
|
+
this.setAppState(appStates.UNMOUNT);
|
|
5296
6714
|
// result of unmount function
|
|
5297
|
-
let umdHookUnmountResult;
|
|
6715
|
+
let umdHookUnmountResult = null;
|
|
5298
6716
|
/**
|
|
5299
6717
|
* send an unmount event to the micro app or call umd unmount hook
|
|
5300
6718
|
* before the sandbox is cleared
|
|
5301
6719
|
*/
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
logError('an error occurred in the unmount function \n', this.name, e);
|
|
5308
|
-
}
|
|
6720
|
+
try {
|
|
6721
|
+
umdHookUnmountResult = (_a = this.umdHookUnmount) === null || _a === void 0 ? void 0 : _a.call(this, microApp.getData(this.name, true));
|
|
6722
|
+
}
|
|
6723
|
+
catch (e) {
|
|
6724
|
+
logError('An error occurred in function unmount \n', this.name, e);
|
|
5309
6725
|
}
|
|
5310
|
-
// call window.onunmount of child app
|
|
5311
|
-
callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONUNMOUNT), this.name, `window.${microGlobalEvent.ONUNMOUNT}`);
|
|
5312
6726
|
// dispatch unmount event to micro app
|
|
5313
|
-
dispatchCustomEventToMicroApp('unmount'
|
|
6727
|
+
dispatchCustomEventToMicroApp(this, 'unmount');
|
|
6728
|
+
// call window.onunmount of child app
|
|
6729
|
+
execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONUNMOUNT), this.name, microGlobalEvent.ONUNMOUNT);
|
|
5314
6730
|
this.handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb);
|
|
5315
6731
|
}
|
|
5316
6732
|
/**
|
|
@@ -5322,19 +6738,17 @@ class CreateApp {
|
|
|
5322
6738
|
* @param unmountcb callback of unmount
|
|
5323
6739
|
*/
|
|
5324
6740
|
handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb) {
|
|
5325
|
-
const
|
|
6741
|
+
const nextAction = () => this.actionsForUnmount({
|
|
5326
6742
|
destroy,
|
|
5327
6743
|
clearData,
|
|
5328
6744
|
keepRouteState,
|
|
5329
6745
|
unmountcb,
|
|
5330
|
-
};
|
|
6746
|
+
});
|
|
5331
6747
|
if (isPromise(umdHookUnmountResult)) {
|
|
5332
|
-
umdHookUnmountResult
|
|
5333
|
-
.then(() => this.actionsForUnmount(unmountParam))
|
|
5334
|
-
.catch(() => this.actionsForUnmount(unmountParam));
|
|
6748
|
+
umdHookUnmountResult.then(nextAction).catch(nextAction);
|
|
5335
6749
|
}
|
|
5336
6750
|
else {
|
|
5337
|
-
|
|
6751
|
+
nextAction();
|
|
5338
6752
|
}
|
|
5339
6753
|
}
|
|
5340
6754
|
/**
|
|
@@ -5345,26 +6759,20 @@ class CreateApp {
|
|
|
5345
6759
|
* @param unmountcb callback of unmount
|
|
5346
6760
|
*/
|
|
5347
6761
|
actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb }) {
|
|
5348
|
-
var _a
|
|
5349
|
-
if (destroy) {
|
|
5350
|
-
this.actionsForCompletelyDestroy();
|
|
5351
|
-
}
|
|
5352
|
-
else if (this.umdMode && this.container.childElementCount) {
|
|
6762
|
+
var _a;
|
|
6763
|
+
if (this.umdMode && this.container && !destroy) {
|
|
5353
6764
|
cloneContainer(this.container, this.source.html, false);
|
|
5354
6765
|
}
|
|
5355
|
-
if (this.umdMode) {
|
|
5356
|
-
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
|
|
5357
|
-
}
|
|
5358
6766
|
/**
|
|
5359
6767
|
* this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
|
|
5360
6768
|
* NOTE:
|
|
5361
6769
|
* 1. if destroy is true, clear route state
|
|
5362
6770
|
* 2. umd mode and keep-alive will not clear EventSource
|
|
5363
6771
|
*/
|
|
5364
|
-
(
|
|
6772
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop({
|
|
5365
6773
|
umdMode: this.umdMode,
|
|
5366
6774
|
keepRouteState: keepRouteState && !destroy,
|
|
5367
|
-
|
|
6775
|
+
destroy,
|
|
5368
6776
|
clearData: clearData || destroy,
|
|
5369
6777
|
});
|
|
5370
6778
|
if (!getActiveApps().length) {
|
|
@@ -5372,59 +6780,67 @@ class CreateApp {
|
|
|
5372
6780
|
}
|
|
5373
6781
|
// dispatch unmount event to base app
|
|
5374
6782
|
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
|
|
5375
|
-
this.
|
|
5376
|
-
unmountcb
|
|
6783
|
+
this.clearOptions(destroy);
|
|
6784
|
+
unmountcb === null || unmountcb === void 0 ? void 0 : unmountcb();
|
|
5377
6785
|
}
|
|
5378
|
-
|
|
6786
|
+
clearOptions(destroy) {
|
|
5379
6787
|
this.container.innerHTML = '';
|
|
5380
6788
|
this.container = null;
|
|
5381
6789
|
this.isPrerender = false;
|
|
5382
|
-
this.
|
|
6790
|
+
this.preRenderEvents = null;
|
|
6791
|
+
this.setKeepAliveState(null);
|
|
6792
|
+
// in iframe sandbox & default mode, delete the sandbox & iframeElement
|
|
6793
|
+
if (this.iframe && !this.umdMode)
|
|
6794
|
+
this.sandBox = null;
|
|
6795
|
+
if (destroy)
|
|
6796
|
+
this.actionsForCompletelyDestroy();
|
|
5383
6797
|
}
|
|
5384
6798
|
// actions for completely destroy
|
|
5385
6799
|
actionsForCompletelyDestroy() {
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
}
|
|
6800
|
+
var _a, _b;
|
|
6801
|
+
(_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.deleteIframeElement) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
5389
6802
|
sourceCenter.script.deleteInlineInfo(this.source.scripts);
|
|
5390
6803
|
appInstanceMap.delete(this.name);
|
|
5391
6804
|
}
|
|
5392
6805
|
// hidden app when disconnectedCallback called with keep-alive
|
|
5393
6806
|
hiddenKeepAliveApp(callback) {
|
|
5394
|
-
var _a;
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
6807
|
+
var _a, _b;
|
|
6808
|
+
this.setKeepAliveState(keepAliveStates.KEEP_ALIVE_HIDDEN);
|
|
6809
|
+
/**
|
|
6810
|
+
* event should dispatch before clone node
|
|
6811
|
+
* dispatch afterHidden event to micro-app
|
|
6812
|
+
*/
|
|
6813
|
+
dispatchCustomEventToMicroApp(this, 'appstate-change', {
|
|
5402
6814
|
appState: 'afterhidden',
|
|
5403
6815
|
});
|
|
5404
6816
|
// dispatch afterHidden event to base app
|
|
5405
|
-
dispatchLifecyclesEvent(
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
6817
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERHIDDEN);
|
|
6818
|
+
if (this.useMemoryRouter) {
|
|
6819
|
+
// called after lifeCyclesEvent
|
|
6820
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
|
|
6821
|
+
}
|
|
6822
|
+
this.container = cloneContainer(this.container, pureCreateElement('div'), false);
|
|
6823
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.recordAndReleaseEffect({ keepAlive: true });
|
|
6824
|
+
callback === null || callback === void 0 ? void 0 : callback();
|
|
5410
6825
|
}
|
|
5411
6826
|
// show app when connectedCallback called with keep-alive
|
|
5412
6827
|
showKeepAliveApp(container) {
|
|
5413
6828
|
var _a, _b;
|
|
5414
6829
|
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
|
|
5415
6830
|
// dispatch beforeShow event to micro-app
|
|
5416
|
-
dispatchCustomEventToMicroApp('appstate-change',
|
|
6831
|
+
dispatchCustomEventToMicroApp(this, 'appstate-change', {
|
|
5417
6832
|
appState: 'beforeshow',
|
|
5418
6833
|
});
|
|
5419
6834
|
// dispatch beforeShow event to base app
|
|
5420
6835
|
dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
|
|
5421
|
-
|
|
5422
|
-
this.container = container;
|
|
5423
|
-
this.
|
|
5424
|
-
|
|
5425
|
-
|
|
6836
|
+
this.setKeepAliveState(keepAliveStates.KEEP_ALIVE_SHOW);
|
|
6837
|
+
this.container = cloneContainer(this.container, container, false);
|
|
6838
|
+
if (this.useMemoryRouter) {
|
|
6839
|
+
// called before lifeCyclesEvent
|
|
6840
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.setRouteInfoForKeepAliveApp();
|
|
6841
|
+
}
|
|
5426
6842
|
// dispatch afterShow event to micro-app
|
|
5427
|
-
dispatchCustomEventToMicroApp('appstate-change',
|
|
6843
|
+
dispatchCustomEventToMicroApp(this, 'appstate-change', {
|
|
5428
6844
|
appState: 'aftershow',
|
|
5429
6845
|
});
|
|
5430
6846
|
// dispatch afterShow event to base app
|
|
@@ -5437,49 +6853,72 @@ class CreateApp {
|
|
|
5437
6853
|
onerror(e) {
|
|
5438
6854
|
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
|
|
5439
6855
|
}
|
|
6856
|
+
/**
|
|
6857
|
+
* Scene:
|
|
6858
|
+
* 1. create app
|
|
6859
|
+
* 2. remount of default mode with iframe sandbox
|
|
6860
|
+
* In default mode with iframe sandbox, unmount app will delete iframeElement & sandBox, and create sandBox when mount again, used to solve the problem that module script cannot be execute when append it again
|
|
6861
|
+
*/
|
|
6862
|
+
createSandbox() {
|
|
6863
|
+
if (this.useSandbox && !this.sandBox) {
|
|
6864
|
+
if (this.iframe) {
|
|
6865
|
+
this.sandBox = new IframeSandbox(this.name, this.url);
|
|
6866
|
+
}
|
|
6867
|
+
else {
|
|
6868
|
+
this.sandBox = new WithSandBox(this.name, this.url);
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
}
|
|
6872
|
+
// set app state
|
|
6873
|
+
setAppState(state) {
|
|
6874
|
+
this.state = state;
|
|
6875
|
+
}
|
|
5440
6876
|
// get app state
|
|
5441
6877
|
getAppState() {
|
|
5442
6878
|
return this.state;
|
|
5443
6879
|
}
|
|
6880
|
+
// set keep-alive state
|
|
6881
|
+
setKeepAliveState(state) {
|
|
6882
|
+
this.keepAliveState = state;
|
|
6883
|
+
}
|
|
5444
6884
|
// get keep-alive state
|
|
5445
6885
|
getKeepAliveState() {
|
|
5446
6886
|
return this.keepAliveState;
|
|
5447
6887
|
}
|
|
5448
6888
|
// get umd library, if it not exist, return empty object
|
|
5449
6889
|
getUmdLibraryHooks() {
|
|
5450
|
-
var _a, _b, _c, _d;
|
|
5451
6890
|
// after execScripts, the app maybe unmounted
|
|
5452
|
-
if (appStates.UNMOUNT !== this.state) {
|
|
5453
|
-
const
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
6891
|
+
if (appStates.UNMOUNT !== this.state && this.sandBox) {
|
|
6892
|
+
const libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
|
|
6893
|
+
const proxyWindow = this.sandBox.proxyWindow;
|
|
6894
|
+
// compatible with pre versions
|
|
6895
|
+
if (isObject(proxyWindow[libraryName])) {
|
|
6896
|
+
return proxyWindow[libraryName];
|
|
5457
6897
|
}
|
|
5458
6898
|
return {
|
|
5459
|
-
mount:
|
|
5460
|
-
unmount:
|
|
6899
|
+
mount: proxyWindow.mount,
|
|
6900
|
+
unmount: proxyWindow.unmount,
|
|
5461
6901
|
};
|
|
5462
6902
|
}
|
|
5463
6903
|
return {};
|
|
5464
6904
|
}
|
|
5465
|
-
|
|
6905
|
+
getMicroAppGlobalHook(eventName) {
|
|
5466
6906
|
var _a;
|
|
5467
|
-
|
|
5468
|
-
const listener = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow[eventName];
|
|
6907
|
+
const listener = ((_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow)[eventName];
|
|
5469
6908
|
return isFunction(listener) ? listener : null;
|
|
5470
6909
|
}
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
*/
|
|
5477
|
-
recordAndReleaseEffect() {
|
|
5478
|
-
var _a, _b;
|
|
5479
|
-
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
|
|
5480
|
-
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.releaseGlobalEffect();
|
|
6910
|
+
querySelector(selectors) {
|
|
6911
|
+
return this.container ? globalEnv.rawElementQuerySelector.call(this.container, selectors) : null;
|
|
6912
|
+
}
|
|
6913
|
+
querySelectorAll(selectors) {
|
|
6914
|
+
return this.container ? globalEnv.rawElementQuerySelectorAll.call(this.container, selectors) : [];
|
|
5481
6915
|
}
|
|
5482
6916
|
}
|
|
6917
|
+
// iframe route mode
|
|
6918
|
+
function isIframeSandbox(appName) {
|
|
6919
|
+
var _a, _b;
|
|
6920
|
+
return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.iframe) !== null && _b !== void 0 ? _b : false;
|
|
6921
|
+
}
|
|
5483
6922
|
|
|
5484
6923
|
/**
|
|
5485
6924
|
* define element
|
|
@@ -5507,12 +6946,14 @@ function defineElement(tagName) {
|
|
|
5507
6946
|
const formatAttrName = formatAppName(this.getAttribute('name'));
|
|
5508
6947
|
const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
|
|
5509
6948
|
if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
|
|
5510
|
-
const
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
6949
|
+
const oldApp = appInstanceMap.get(formatAttrName);
|
|
6950
|
+
/**
|
|
6951
|
+
* If oldApp exist & appName is different, determine whether oldApp is running
|
|
6952
|
+
*/
|
|
6953
|
+
if (formatAttrName !== this.appName && oldApp) {
|
|
6954
|
+
if (oldApp.getAppState() !== appStates.UNMOUNT &&
|
|
6955
|
+
oldApp.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN &&
|
|
6956
|
+
!oldApp.isPrefetch) {
|
|
5516
6957
|
this.setAttribute('name', this.appName);
|
|
5517
6958
|
return logError(`app name conflict, an app named ${formatAttrName} is running`);
|
|
5518
6959
|
}
|
|
@@ -5520,16 +6961,16 @@ function defineElement(tagName) {
|
|
|
5520
6961
|
if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
|
|
5521
6962
|
if (formatAttrName === this.appName) {
|
|
5522
6963
|
this.handleUnmount(true, () => {
|
|
5523
|
-
this.actionsForAttributeChange(formatAttrName, formatAttrUrl,
|
|
6964
|
+
this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
|
|
5524
6965
|
});
|
|
5525
6966
|
}
|
|
5526
6967
|
else if (this.getKeepAliveModeResult()) {
|
|
5527
6968
|
this.handleHiddenKeepAliveApp();
|
|
5528
|
-
this.actionsForAttributeChange(formatAttrName, formatAttrUrl,
|
|
6969
|
+
this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
|
|
5529
6970
|
}
|
|
5530
6971
|
else {
|
|
5531
6972
|
this.handleUnmount(this.getDestroyCompatibleResult(), () => {
|
|
5532
|
-
this.actionsForAttributeChange(formatAttrName, formatAttrUrl,
|
|
6973
|
+
this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
|
|
5533
6974
|
});
|
|
5534
6975
|
}
|
|
5535
6976
|
}
|
|
@@ -5561,10 +7002,16 @@ function defineElement(tagName) {
|
|
|
5561
7002
|
* In some special scenes, such as vue's keep-alive, the micro-app will be inserted and deleted twice in an instant
|
|
5562
7003
|
* So we execute the mount method async and record connectState to prevent repeated rendering
|
|
5563
7004
|
*/
|
|
7005
|
+
const effectiveApp = this.appName && this.appUrl;
|
|
5564
7006
|
defer(() => {
|
|
5565
7007
|
if (this.connectStateMap.get(cacheCount)) {
|
|
5566
7008
|
dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED);
|
|
5567
|
-
|
|
7009
|
+
/**
|
|
7010
|
+
* If insert micro-app element without name or url, and set them in next action like angular,
|
|
7011
|
+
* handleConnected will be executed twice, causing the app render repeatedly,
|
|
7012
|
+
* so we only execute handleConnected() if url and name exist when connectedCallback
|
|
7013
|
+
*/
|
|
7014
|
+
effectiveApp && this.handleConnected();
|
|
5568
7015
|
}
|
|
5569
7016
|
});
|
|
5570
7017
|
}
|
|
@@ -5655,37 +7102,35 @@ function defineElement(tagName) {
|
|
|
5655
7102
|
}
|
|
5656
7103
|
this.updateSsrUrl(this.appUrl);
|
|
5657
7104
|
if (appInstanceMap.has(this.appName)) {
|
|
5658
|
-
const
|
|
5659
|
-
const
|
|
5660
|
-
const
|
|
7105
|
+
const oldApp = appInstanceMap.get(this.appName);
|
|
7106
|
+
const oldAppUrl = oldApp.ssrUrl || oldApp.url;
|
|
7107
|
+
const targetUrl = this.ssrUrl || this.appUrl;
|
|
5661
7108
|
/**
|
|
5662
7109
|
* NOTE:
|
|
5663
7110
|
* 1. keep-alive don't care about ssrUrl
|
|
5664
7111
|
* 2. Even if the keep-alive app is pushed into the background, it is still active and cannot be replaced. Otherwise, it is difficult for developers to troubleshoot in case of conflict and will leave developers at a loss
|
|
5665
7112
|
* 3. When scopecss, useSandbox of prefetch app different from target app, delete prefetch app and create new one
|
|
5666
7113
|
*/
|
|
5667
|
-
if (
|
|
5668
|
-
|
|
5669
|
-
this.handleShowKeepAliveApp(
|
|
7114
|
+
if (oldApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
|
|
7115
|
+
oldApp.url === this.appUrl) {
|
|
7116
|
+
this.handleShowKeepAliveApp(oldApp);
|
|
5670
7117
|
}
|
|
5671
|
-
else if (
|
|
5672
|
-
(
|
|
5673
|
-
|
|
5674
|
-
this.handleAppMount(
|
|
7118
|
+
else if (oldAppUrl === targetUrl && (oldApp.getAppState() === appStates.UNMOUNT ||
|
|
7119
|
+
(oldApp.isPrefetch &&
|
|
7120
|
+
this.sameCoreOptions(oldApp)))) {
|
|
7121
|
+
this.handleAppMount(oldApp);
|
|
5675
7122
|
}
|
|
5676
|
-
else if (
|
|
5677
|
-
if ((process.env.NODE_ENV !== 'production') &&
|
|
5678
|
-
app.scopecss === this.isScopecss() &&
|
|
5679
|
-
app.useSandbox === this.isSandbox()) {
|
|
7123
|
+
else if (oldApp.isPrefetch || oldApp.getAppState() === appStates.UNMOUNT) {
|
|
7124
|
+
if ((process.env.NODE_ENV !== 'production') && this.sameCoreOptions(oldApp)) {
|
|
5680
7125
|
/**
|
|
5681
7126
|
* url is different & old app is unmounted or prefetch, create new app to replace old one
|
|
5682
7127
|
*/
|
|
5683
|
-
logWarn(`the ${
|
|
7128
|
+
logWarn(`the ${oldApp.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${oldAppUrl} replaced by a new app with url: ${targetUrl}`, this.appName);
|
|
5684
7129
|
}
|
|
5685
7130
|
this.handleCreateApp();
|
|
5686
7131
|
}
|
|
5687
7132
|
else {
|
|
5688
|
-
logError(`app name conflict, an app named: ${this.appName} with url: ${
|
|
7133
|
+
logError(`app name conflict, an app named: ${this.appName} with url: ${oldAppUrl} is running`);
|
|
5689
7134
|
}
|
|
5690
7135
|
}
|
|
5691
7136
|
else {
|
|
@@ -5693,7 +7138,7 @@ function defineElement(tagName) {
|
|
|
5693
7138
|
}
|
|
5694
7139
|
}
|
|
5695
7140
|
// remount app or create app if attribute url or name change
|
|
5696
|
-
actionsForAttributeChange(formatAttrName, formatAttrUrl,
|
|
7141
|
+
actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp) {
|
|
5697
7142
|
var _a;
|
|
5698
7143
|
/**
|
|
5699
7144
|
* do not add judgment of formatAttrUrl === this.appUrl
|
|
@@ -5706,26 +7151,38 @@ function defineElement(tagName) {
|
|
|
5706
7151
|
this.setAttribute('name', this.appName);
|
|
5707
7152
|
}
|
|
5708
7153
|
/**
|
|
5709
|
-
* when
|
|
5710
|
-
* scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different,
|
|
5711
|
-
* scene2: if formatAttrName and this.appName are different:
|
|
7154
|
+
* when oldApp not null: this.appName === oldApp.name
|
|
7155
|
+
* scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, oldApp has been unmounted
|
|
7156
|
+
* scene2: if formatAttrName and this.appName are different: oldApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace oldApp
|
|
5712
7157
|
* scene3: url is different but ssrUrl is equal
|
|
5713
7158
|
* scene4: url is equal but ssrUrl is different, if url is equal, name must different
|
|
5714
|
-
* scene5: if
|
|
7159
|
+
* scene5: if oldApp is KEEP_ALIVE_HIDDEN, name must different
|
|
5715
7160
|
*/
|
|
5716
|
-
if (
|
|
5717
|
-
if (
|
|
5718
|
-
if (
|
|
5719
|
-
this.handleShowKeepAliveApp(
|
|
7161
|
+
if (oldApp) {
|
|
7162
|
+
if (oldApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
|
|
7163
|
+
if (oldApp.url === this.appUrl) {
|
|
7164
|
+
this.handleShowKeepAliveApp(oldApp);
|
|
5720
7165
|
}
|
|
5721
7166
|
else {
|
|
5722
7167
|
// the hidden keep-alive app is still active
|
|
5723
7168
|
logError(`app name conflict, an app named ${this.appName} is running`);
|
|
5724
7169
|
}
|
|
7170
|
+
/**
|
|
7171
|
+
* TODO:
|
|
7172
|
+
* 1. oldApp必是unmountApp或preFetchApp,这里还应该考虑沙箱、iframe、样式隔离不一致的情况
|
|
7173
|
+
* 2. unmountApp要不要判断样式隔离、沙箱、iframe,然后彻底删除并再次渲染?(包括handleConnected里的处理,先不改?)
|
|
7174
|
+
* 推荐:if (
|
|
7175
|
+
* oldApp.url === this.appUrl &&
|
|
7176
|
+
* oldApp.ssrUrl === this.ssrUrl && (
|
|
7177
|
+
* oldApp.getAppState() === appStates.UNMOUNT ||
|
|
7178
|
+
* (oldApp.isPrefetch && this.sameCoreOptions(oldApp))
|
|
7179
|
+
* )
|
|
7180
|
+
* )
|
|
7181
|
+
*/
|
|
5725
7182
|
}
|
|
5726
|
-
else if (
|
|
7183
|
+
else if (oldApp.url === this.appUrl && oldApp.ssrUrl === this.ssrUrl) {
|
|
5727
7184
|
// mount app
|
|
5728
|
-
this.handleAppMount(
|
|
7185
|
+
this.handleAppMount(oldApp);
|
|
5729
7186
|
}
|
|
5730
7187
|
else {
|
|
5731
7188
|
this.handleCreateApp();
|
|
@@ -5749,24 +7206,39 @@ function defineElement(tagName) {
|
|
|
5749
7206
|
}
|
|
5750
7207
|
// create app instance
|
|
5751
7208
|
handleCreateApp() {
|
|
5752
|
-
|
|
7209
|
+
const createAppInstance = () => {
|
|
7210
|
+
var _a;
|
|
7211
|
+
return new CreateApp({
|
|
7212
|
+
name: this.appName,
|
|
7213
|
+
url: this.appUrl,
|
|
7214
|
+
scopecss: this.useScopecss(),
|
|
7215
|
+
useSandbox: this.useSandbox(),
|
|
7216
|
+
inline: this.getDisposeResult('inline'),
|
|
7217
|
+
iframe: this.getDisposeResult('iframe'),
|
|
7218
|
+
container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
|
|
7219
|
+
ssrUrl: this.ssrUrl,
|
|
7220
|
+
});
|
|
7221
|
+
};
|
|
5753
7222
|
/**
|
|
5754
|
-
*
|
|
5755
|
-
*
|
|
7223
|
+
* Actions for destroy old app
|
|
7224
|
+
* If oldApp exist, it must be 3 scenes:
|
|
7225
|
+
* 1. oldApp is unmounted app (url is is different)
|
|
7226
|
+
* 2. oldApp is prefetch, not prerender (url, scopecss, useSandbox, iframe is different)
|
|
7227
|
+
* 3. oldApp is prerender (url, scopecss, useSandbox, iframe is different)
|
|
5756
7228
|
*/
|
|
5757
|
-
|
|
5758
|
-
|
|
7229
|
+
const oldApp = appInstanceMap.get(this.appName);
|
|
7230
|
+
if (oldApp) {
|
|
7231
|
+
if (oldApp.isPrerender) {
|
|
7232
|
+
this.handleUnmount(true, createAppInstance);
|
|
7233
|
+
}
|
|
7234
|
+
else {
|
|
7235
|
+
oldApp.actionsForCompletelyDestroy();
|
|
7236
|
+
createAppInstance();
|
|
7237
|
+
}
|
|
7238
|
+
}
|
|
7239
|
+
else {
|
|
7240
|
+
createAppInstance();
|
|
5759
7241
|
}
|
|
5760
|
-
new CreateApp({
|
|
5761
|
-
name: this.appName,
|
|
5762
|
-
url: this.appUrl,
|
|
5763
|
-
scopecss: this.isScopecss(),
|
|
5764
|
-
useSandbox: this.isSandbox(),
|
|
5765
|
-
inline: this.getDisposeResult('inline'),
|
|
5766
|
-
esmodule: this.getDisposeResult('esmodule'),
|
|
5767
|
-
container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
|
|
5768
|
-
ssrUrl: this.ssrUrl,
|
|
5769
|
-
});
|
|
5770
7242
|
}
|
|
5771
7243
|
/**
|
|
5772
7244
|
* mount app
|
|
@@ -5792,7 +7264,6 @@ function defineElement(tagName) {
|
|
|
5792
7264
|
baseroute: this.getBaseRouteCompatible(),
|
|
5793
7265
|
disablePatchRequest: this.getDisposeResult('disable-patch-request'),
|
|
5794
7266
|
fiber: this.getDisposeResult('fiber'),
|
|
5795
|
-
esmodule: this.getDisposeResult('esmodule'),
|
|
5796
7267
|
});
|
|
5797
7268
|
}
|
|
5798
7269
|
/**
|
|
@@ -5831,10 +7302,10 @@ function defineElement(tagName) {
|
|
|
5831
7302
|
* @param name Configuration item name
|
|
5832
7303
|
*/
|
|
5833
7304
|
getDisposeResult(name) {
|
|
5834
|
-
return (this.
|
|
7305
|
+
return (this.compatibleProperties(name) || !!microApp.options[name]) && this.compatibleDisableProperties(name);
|
|
5835
7306
|
}
|
|
5836
7307
|
// compatible of disableScopecss & disableSandbox
|
|
5837
|
-
|
|
7308
|
+
compatibleProperties(name) {
|
|
5838
7309
|
if (name === 'disable-scopecss') {
|
|
5839
7310
|
return this.hasAttribute('disable-scopecss') || this.hasAttribute('disableScopecss');
|
|
5840
7311
|
}
|
|
@@ -5844,7 +7315,7 @@ function defineElement(tagName) {
|
|
|
5844
7315
|
return this.hasAttribute(name);
|
|
5845
7316
|
}
|
|
5846
7317
|
// compatible of disableScopecss & disableSandbox
|
|
5847
|
-
|
|
7318
|
+
compatibleDisableProperties(name) {
|
|
5848
7319
|
if (name === 'disable-scopecss') {
|
|
5849
7320
|
return this.getAttribute('disable-scopecss') !== 'false' && this.getAttribute('disableScopecss') !== 'false';
|
|
5850
7321
|
}
|
|
@@ -5853,12 +7324,20 @@ function defineElement(tagName) {
|
|
|
5853
7324
|
}
|
|
5854
7325
|
return this.getAttribute(name) !== 'false';
|
|
5855
7326
|
}
|
|
5856
|
-
|
|
7327
|
+
useScopecss() {
|
|
5857
7328
|
return !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM'));
|
|
5858
7329
|
}
|
|
5859
|
-
|
|
7330
|
+
useSandbox() {
|
|
5860
7331
|
return !this.getDisposeResult('disable-sandbox');
|
|
5861
7332
|
}
|
|
7333
|
+
/**
|
|
7334
|
+
* Determine whether the core options of the existApp is consistent with the new one
|
|
7335
|
+
*/
|
|
7336
|
+
sameCoreOptions(app) {
|
|
7337
|
+
return (app.scopecss === this.useScopecss() &&
|
|
7338
|
+
app.useSandbox === this.useSandbox() &&
|
|
7339
|
+
app.iframe === this.getDisposeResult('iframe'));
|
|
7340
|
+
}
|
|
5862
7341
|
/**
|
|
5863
7342
|
* 2021-09-08
|
|
5864
7343
|
* get baseRoute
|
|
@@ -5943,23 +7422,32 @@ function defineElement(tagName) {
|
|
|
5943
7422
|
* {
|
|
5944
7423
|
* name: string,
|
|
5945
7424
|
* url: string,
|
|
5946
|
-
*
|
|
5947
|
-
*
|
|
5948
|
-
*
|
|
7425
|
+
* iframe: boolean,
|
|
7426
|
+
* inline: boolean,
|
|
7427
|
+
* 'disable-scopecss': boolean,
|
|
7428
|
+
* 'disable-sandbox': boolean,
|
|
7429
|
+
* level: number,
|
|
7430
|
+
* 'default-page': string,
|
|
7431
|
+
* 'disable-patch-request': boolean,
|
|
5949
7432
|
* },
|
|
5950
7433
|
* ...
|
|
5951
7434
|
* ])
|
|
5952
7435
|
* Note:
|
|
5953
|
-
* 1: preFetch is
|
|
5954
|
-
* 2:
|
|
5955
|
-
* @param apps micro
|
|
7436
|
+
* 1: preFetch is async and is performed only when the browser is idle
|
|
7437
|
+
* 2: options of prefetch preferably match the config of the micro-app element, although this is not required
|
|
7438
|
+
* @param apps micro app options
|
|
7439
|
+
* @param delay delay time
|
|
5956
7440
|
*/
|
|
5957
7441
|
function preFetch(apps, delay) {
|
|
5958
7442
|
if (!isBrowser) {
|
|
5959
7443
|
return logError('preFetch is only supported in browser environment');
|
|
5960
7444
|
}
|
|
5961
7445
|
requestIdleCallback(() => {
|
|
5962
|
-
const delayTime = delay
|
|
7446
|
+
const delayTime = isNumber(delay) ? delay : microApp.options.prefetchDelay;
|
|
7447
|
+
/**
|
|
7448
|
+
* TODO: remove setTimeout
|
|
7449
|
+
* Is there a better way?
|
|
7450
|
+
*/
|
|
5963
7451
|
setTimeout(() => {
|
|
5964
7452
|
// releasePrefetchEffect()
|
|
5965
7453
|
preFetchInSerial(apps);
|
|
@@ -5998,7 +7486,7 @@ function preFetchAction(options) {
|
|
|
5998
7486
|
scopecss: !((_b = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss) !== null && _b !== void 0 ? _b : microApp.options['disable-scopecss']),
|
|
5999
7487
|
useSandbox: !((_d = (_c = options['disable-sandbox']) !== null && _c !== void 0 ? _c : options.disableSandbox) !== null && _d !== void 0 ? _d : microApp.options['disable-sandbox']),
|
|
6000
7488
|
inline: (_e = options.inline) !== null && _e !== void 0 ? _e : microApp.options.inline,
|
|
6001
|
-
|
|
7489
|
+
iframe: (_f = options.iframe) !== null && _f !== void 0 ? _f : microApp.options.iframe,
|
|
6002
7490
|
prefetchLevel: options.level && PREFETCH_LEVEL.includes(options.level) ? options.level : microApp.options.prefetchLevel && PREFETCH_LEVEL.includes(microApp.options.prefetchLevel) ? microApp.options.prefetchLevel : 2,
|
|
6003
7491
|
});
|
|
6004
7492
|
const oldOnload = app.onLoad;
|
|
@@ -6097,10 +7585,19 @@ function unmountApp(appName, options) {
|
|
|
6097
7585
|
return new Promise((resolve) => {
|
|
6098
7586
|
if (app) {
|
|
6099
7587
|
if (app.getAppState() === appStates.UNMOUNT || app.isPrefetch) {
|
|
6100
|
-
if (
|
|
6101
|
-
app.
|
|
7588
|
+
if (app.isPrerender) {
|
|
7589
|
+
app.unmount({
|
|
7590
|
+
destroy: !!(options === null || options === void 0 ? void 0 : options.destroy),
|
|
7591
|
+
clearData: !!(options === null || options === void 0 ? void 0 : options.clearData),
|
|
7592
|
+
keepRouteState: false,
|
|
7593
|
+
unmountcb: resolve.bind(null, true)
|
|
7594
|
+
});
|
|
7595
|
+
}
|
|
7596
|
+
else {
|
|
7597
|
+
if (options === null || options === void 0 ? void 0 : options.destroy)
|
|
7598
|
+
app.actionsForCompletelyDestroy();
|
|
7599
|
+
resolve(true);
|
|
6102
7600
|
}
|
|
6103
|
-
resolve(true);
|
|
6104
7601
|
}
|
|
6105
7602
|
else if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
|
|
6106
7603
|
if (options === null || options === void 0 ? void 0 : options.destroy) {
|
|
@@ -6150,7 +7647,7 @@ function unmountApp(appName, options) {
|
|
|
6150
7647
|
else if ((options === null || options === void 0 ? void 0 : options.clearAliveState) && container.hasAttribute('keep-alive')) {
|
|
6151
7648
|
const keepAliveAttrValue = container.getAttribute('keep-alive');
|
|
6152
7649
|
container.removeAttribute('keep-alive');
|
|
6153
|
-
let clearDataAttrValue;
|
|
7650
|
+
let clearDataAttrValue = null;
|
|
6154
7651
|
if (options.clearData) {
|
|
6155
7652
|
clearDataAttrValue = container.getAttribute('clear-data');
|
|
6156
7653
|
container.setAttribute('clear-data', 'true');
|
|
@@ -6160,7 +7657,7 @@ function unmountApp(appName, options) {
|
|
|
6160
7657
|
isString(clearDataAttrValue) && container.setAttribute('clear-data', clearDataAttrValue);
|
|
6161
7658
|
}
|
|
6162
7659
|
else {
|
|
6163
|
-
let clearDataAttrValue;
|
|
7660
|
+
let clearDataAttrValue = null;
|
|
6164
7661
|
if (options === null || options === void 0 ? void 0 : options.clearData) {
|
|
6165
7662
|
clearDataAttrValue = container.getAttribute('clear-data');
|
|
6166
7663
|
container.setAttribute('clear-data', 'true');
|
|
@@ -6283,10 +7780,10 @@ class MicroApp extends EventCenterForBaseApp {
|
|
|
6283
7780
|
return logError(`${options.tagName} is invalid tagName`);
|
|
6284
7781
|
}
|
|
6285
7782
|
}
|
|
6286
|
-
|
|
7783
|
+
initGlobalEnv();
|
|
7784
|
+
if (globalEnv.rawWindow.customElements.get(this.tagName)) {
|
|
6287
7785
|
return logWarn(`element ${this.tagName} is already defined`);
|
|
6288
7786
|
}
|
|
6289
|
-
initGlobalEnv();
|
|
6290
7787
|
if (isPlainObject(options)) {
|
|
6291
7788
|
this.options = options;
|
|
6292
7789
|
options['disable-scopecss'] = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss;
|