Elegantly implement a new micro front-end solution based on iframe

What is micro frontend

Micro front-end is an architecture similar to microservices. It applies the concept of microservices to the browser side, that is, it transforms a Web application from a single application into an application in which multiple small front-end applications are aggregated into one. Each front-end application can also be run independently, developed independently, and deployed independently.

Simply put, it uses a series of tools and technologies to assemble the UI pages of each team into a coherent application that users can use.

Micro frontend is a concept that has become popular in recent years. iframe is an ideal solution for early implementation of micro frontend. Now there are other solutions, such as qianduan framework, single-spa, and the federated module solution brought by webpack5. However, each solution has its advantages and disadvantages, and those who are interested can try it out.

iframe’s new micro front-end solution

iframe is a natural micro front-end solution, but it cannot be applied well due to strict cross-domain restrictions. This article introduces an elegant implementation of a new micro front-end solution based on iframe, inheriting the advantages of iframe, complementing the shortcomings of iframe, and allowing iframes take on a new lease of life.

We are already very familiar with iframes in front-end development, so what is the role of iframes? It can be summarized as follows:

When using iframe before, another page was introduced for rendering during the use of the page, so its basic function is: to run another web application independently in a web application.

Advantages of using iframe

  • Very simple, no mental burden to use
  • The isolation is perfect, whether it is js, css, or dom, they are completely isolated.
  • Multi-application activation, multiple iframes can be placed on the page to combine services

Disadvantages of using iframe

  • The routing status is lost. If you refresh it, the url status of the iframe will be lost.
  • The DOM is severely fragmented. Pop-ups can only be displayed inside the iframe and cannot cover the entire world.
  • Communication is very difficult, only serialized messages can be delivered via postmessage

Can you create a perfect iframe that retains all the advantages and solves all the disadvantages?

This article takes vue2 as an example to build an iframe framework project for secondary navigation of left navigation and top navigation.

Implementation steps

1. First create a base project. The project only has a navigation frame, no pages, and no routing.

All functions are implemented in App.vue

2. Write the structure directly in App.vue
<div class="el-container">
    <div class="el-menu">左侧一级导航</div>
    <div class="el-main">
        <div class="el-header">右侧顶部二级导航</div>
        <div class="el-aside" id="iframeBox">iframe的容器</div>
    </div>
</div>
3. Render navigation data

Here is the data part

data() {
    return {
      index1: 0, //一级导航当前下标
      index2: 0, //二级导航当前下标
      forWard: true, //是否记录路由
      isOpen: false, //是否打开弹窗
      menuTree: [
        {
          id: "1",
          name: "menu1",
          order: 10,
          subMenu: [
            {
              id: "11",
              name: "mneu1_sub1",
              order: 11,
              subMenu: null,
              text: "二级菜单11",
              url: "http://127.0.0.1:5500/js/alert.html",
            },
          ],
          text: "一级菜单1",
          url: "",
        },
      ],
    };
4. Add iframe tag

When the navigation is clicked, this event is triggered and the iframe tag is dynamically added.

//显示iframe
replaceUrlFun() {
  const bigNode = document.getElementById("iframeBox");
  const contentIframe = document.getElementById("contentIframe");
  if (contentIframe) {
    contentIframe.remove();
  }
  const { index1, index2, menuTree } = this;
  const url1 = menuTree[index1].url;
  const url2 = menuTree[index1].subMenu?.[index2]?.url || "";
  let url = url2 || url1;
  const iframeCon = document.createElement("iframe");
  bigNode.appendChild(iframeCon);
  iframeCon.setAttribute("class", "iframe");
  iframeCon.setAttribute("id", "contentIframe");
  iframeCon.setAttribute("frameborder", 0);
  iframeCon.setAttribute("allowfullscreen", true);
  iframeCon.src = url;
},

Some issues that need to be resolved

1. Communication between projects

Use the postMessage method to complete communication between the base project and subprojects.

2. Problems with pop-up windows and mask layers of iframes

Write the pop-up layer code into the parent page, and the child page uses the postMessage method to send a message to tell the parent page to open and close the pop-up layer, and the parent page listener is turned on and the pop-up layer is closed.

<!-- 父页面弹窗蒙层 -->
<div class="openDiv" v-if="isOpen"></div>

//监听子应用消息
window.addEventListener(
  "message",
  function (event) {
    if (event.data == "openDiv") {
      that.openDiv();
    }
    if (event.data == "closeDiv") {
      that.closeDiv();
    }
  },
  false
);

Encountered a problem: the pop-up layer of the parent page will cover the entire iframe.

solution:

1. Set the pop-up layer of the parent page to position: fixed;z-index: 100;
2. Set ifame to position: relative;z-index: 200;
3. Set a mask layer for the sub-page to cover the ifame area. position: fixed;z-index: 300;
4. Set the pop-up window container, position: fixed; z-index: 400;
5. Sub-page events, open the pop-up window, send messages to the parent page
6. Parent page events, listen for messages , open the pop-up window

<div class="box">
    <button @click="openDiv">弹窗</button>
</div>
<!-- 子页面弹窗蒙层 -->
<div class="openDiv" v-if="isOpen">
    <div class="openBox" @click="closeDiv">点击关闭</div>
</div>
// 子页面事件
openDiv() {
    // 向父窗口发送消息
    window.top.postMessage('openDiv', '*');
    this.isOpen = true;
},
closeDiv() {
    // 向父窗口发送消息
    window.top.postMessage('closeDiv', '*');
    this.isOpen = false;
},
3. Full screen problem in iframe

Full-screen solution, the native method uses Element.requestFullscreen(), and the iframe tag sets the allowfullscreen="true" attribute

//全屏事件
fullscreen() {
    const bigNode = document.getElementById("app");
    bigNode.requestFullscreen()
}
4. Component reuse problem

Public components can be separately proposed and put into a separate project, and all public components can be exposed in the project for installation and use by other projects. In other words, the main project and sub-projects can selectively install the required components.

5. Browser back issue

The iframe and the main page share a browsing history, and the iframe will affect the forward and backward movement of the page. And the iframe page refresh will reset, because the browser's address bar has not changed, and the iframe's src has not changed either.

Although the jump outside the iframe page will not change the browser address bar, it will generate an invisible "history record", that is, click the back or back button (history.forward() or history.back()) You can make the iframe page go back and forth, but there will be no change in the address bar.

So to be precise, we don’t need to do anything to go back. All we have to do is let the browser address bar update synchronously.

When the navigation is clicked, this event is triggered and the URL is updated.
//记录路由方法
routerKeyArrFun() {
  if (!this.forWard) return;
  const { index1, index2, menuTree } = this;
  const name1 = menuTree[index1]?.name || "";
  const name2 = menuTree[index1]?.subMenu?.[index2]?.name || "";
  if (name1) {
    const url = `#/?${name1}${name2 ? `#${name2}` : ""}`;
    history.pushState(null, null, url);
  }
},
6. Refresh problem

To ensure URL synchronization updates, these three conditions need to be met:

  • The page is refreshed and the iframe can load the correct page;
  • The page jumps and the browser address bar may be updated correctly;
  • Click the browser's back button, and the address bar and iframe may change simultaneously;
Above we have recorded the routing information and updated the URL address. So whenever you refresh or rewind, just parse the URL.
//解析浏览器信息
getUrlModuleInfo() {
  this.forWard = false; //记录地址开关
  const { menuTree } = this;
  const ohref = window.location.href;
  const len = ohref.indexOf("?");
  if (len < 0) {
    this.selectFirstMenu();
    this.forWard = true;
    return;
  }
  const params = ohref.substring(len + 1).split("#");
  const [first, second] = params;
  menuTree.forEach((item, index) => {
    if (item.name !== first) return;
    this.selectFirstMenu(index);
    if (!second) return;
    item.subMenu?.forEach((sub, i) => {
      if (sub.name !== second) return;
      this.selectSecondMenu(i);
    });
  });
  this.forWard = true;
},
7. Achieve login-free sub-applications
7.1. Cross-domain cookie sharing

Under the XMLHttpRequest v2 standard, the CORS (Cross Origin Resourse-Sharing) model is proposed in an attempt to provide safe and convenient cross-domain reading and writing resources. Currently, all major browsers support CORS. (IE10+)

7.2. Agent cross-domain sharing cookies

When our request needs to go through the proxy server, we can forward the request to different ports under the same domain name, so that cookies can be shared. For example, assuming that the server is on port 8080 and we need to share cookies, we can use Nginx to configure a reverse proxy to achieve this.

server {
    listen 80;
    server_name xxx.com;
    location / {
        proxy_pass http://127.0.0.1:8080/;
        proxy_set_header Host $host;
    }
}
7.3、Json Web Token

In projects with separate front-end and back-end, you can use JWT (Json Web Token) for authentication and use it instead of Cookie to achieve cross-domain sharing.

Interface effect

Insert image description here

Guess you like

Origin blog.csdn.net/shanghai597/article/details/131764952