scenario
As shown in the figure below, iframe is embedded in the parent window. There are two buttons in iframe: Log out and wake up. Click log out to return to the login page of the parent window, and wake up will call the sweep of the parent window.
Since the iframe and the parent window are from different sources, you cannot directly manipulate the parent window because you cannot get it. So use postMessage for this scenario. Now let’s talk about how to implement the specific scenario operation.
postMessage-MDN
Communication the SDK
We mainly solve two problems:
- How does the iframe communicate with the parent window, such as sending the parent window back to the login page
- Each time postMessage calls the methods of the parent window, the data is in the form of a Promise
The specific implementation
- Definition to provide for
iframe
The method of iframe
The bindingmessage
The eventiframe
Need to import the parent window providedSDK
Definition to provide foriframe
The method of
The parent window needs to provide methods to iframe, such as navigateToLogin and scanQRCore
// ACTIONS.js
export default {
async navigateToLogin(query) {
await store.$dispatch('user/logout, query');
return router.push({
path: '/login',
query,
})
},
async scanQRCore(query) {
return await JsBridge.call({
type: 'SCAN:CODE', query }); }}Copy the code
iframe
The bindingmessage
The event
The VUE component is used as an example to bind iframe message events in the component.
<template>
<iframe ref="merchant">
</iframe>
</template>
<script>
import ACTION_MAP from './ACTIONS';
export default {
created() {
window.addEventListener('message'.this.messageListener);
this.$once('hook:beforeDestroy'.() = > {
window.removeEventListener('message'.this.messageListener);
});
},
methods: {
async messageListener({ data }) {
const iframe = this.$refs.merchant;
try {
const { action, params } = data;
if (action && ACTION_MAP[action]) {
console.log('the SDK events', action, 'parameters'.JSON.stringify(params));
try {
const response = await ACTION_MAP[action](params);
iframe.contentWindow.postMessage({
action,
response
});
} catch (error) {
iframe.contentWindow.postMessage({
action,
response: Promise.reject(error) }); }}}catch (error) {}
}
}
};
</script>
Copy the code
iframe
Need to import the parent window providedSDK
The sdK.js that is eventually exposed to iframe, we want each API of the SDK to return a Promise, so the resolve of the Promise is determined by the postMessage timing of the parent window. The communication mode is as follows
// https://parent.com/helpers/sdk.js! (function(root, fatctory) { root.SDK = fatctory(); }) (window.function() {
let topWindow = null;
const APIS = {};
const ACTION_MAP = [
'scanQRCore'.'navigateToLogin',
].reduce((r, c) = > (r[c] = c) && r, {});
// Make sure iframe is loaded, otherwise topWindow may still be null
const loadPromise = new Promise((resolve, reject) = > {
window.addEventListener('load'.() = > {
resolve();
topWindow = window.top || window.parent;
window.addEventListener('message'.e= > {
const data = e.data;
try {
const { action, response } = data;
Resolve the API if a postMessage from the parent window is received
if(action) { APIS[action].rs(response); }}catch (error) {}
});
});
});
const postMessagePromise = (action, params = {}) = > {
return new Promise((rs, rj) = > {
// When will each API resolve?
APIS[action] = { rs, rj };
loadPromise.then(() = > {
SDK.postMessage({
action,
params
});
});
});
};
const SDK = {
[ACTION_MAP.postMessage](data) {
data.action = data.action || ACTION_MAP.postMessage;
// Send a message to the parent window
topWindow && topWindow.postMessage(data, The '*');
},
// Each API returns a Promise
[ACTION_MAP.scanQRCore](data) {
return postMessagePromise(ACTION_MAP.scanQRCore, data);
},
[ACTION_MAP.navigateToLogin](data) {
returnpostMessagePromise(ACTION_MAP.navigateToLogin, data); }},return SDK;
});
Copy the code
The sdK.js above contains the Promise syntax, which requires the introduction of es-Promise polyfill when packaged using rollup
Introduction, you can complete and parent window communication.
<! DOCTYPEhtml>
<html lang="en">
<head>
<script src="https://parent.com/helpers/sdk.js"></script>
</head>
<body>
</body>
</html>
Copy the code
why not qiankun
Iframe is the most suitable and inherently isolated mechanism for a micro front end, regardless of the experience issues, but using iframe has the following pain points
- Cookies cannot be carried across domains, and child applications deal with the hassle of doing free boarding
- The UI is not synchronized and the DOM structure is not shared. In iframe, a popbox that is centered vertically and horizontally relative to the page is computed by the parent window position
- The URL is not synchronized. The browser refresh iframe URL status is lost, and the back forward button is unavailable
- PostMessage can transmit only strings of data
The above problems, perhaps many are the development and experience of some problems, although there are some means to hack at the end, but this main application and iframe application are tired to death. Why do we still use the IFrame scheme? The main reason is that it is a burden of history. There are too many related applications, and the business side refuses to change it. After all, this thing is an optimization plan, well done without praise, the experience is not much difference, if the problem who to blame?