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 foriframeThe method of
  • iframeThe bindingmessageThe event
  • iframeNeed to import the parent window providedSDK

Definition to provide foriframeThe 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

iframeThe bindingmessageThe 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

iframeNeed 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?