微前端之 qiankun 实战

微前端概述

微前端就是将应用拆分成多个子应用,主应用再加载这些子应用。核心就是先拆后合。

  1. 微前端解决的问题
  • 技术栈不同
  • 独立开发,独立部署
  1. 微前端方案
  • iframe:通过iframe嵌入子应用,通过postMessage进行通信,完美的沙箱机制。缺点:弹窗只能在iframe中,url不能体现在地址栏。
  • single-spa:通过SystemJS加载子应用,没有沙箱机制
  • qiankun:对single-spa封装,提供了沙箱机制
  • 模块联邦:将组件打包导出,需要使用webpack5
  • 其它:EMP(百度),MicroApp (京东),wujie(腾讯)

qiankun 实战

主应用

  1. 创建主应用:yarn create react-app
  2. 添加路由:yarn add react-router-dom
import { BrowserRouter, Link } from "react-router-dom";

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Link to="/react">react</Link>
        <span> </span>
        <Link to="/static">static</Link>
      </BrowserRouter>
      <div id="subapp"></div>
    </div>
  );
}

export default App;
  1. 安装qiankun:yarn add qiankun
  2. 注册微应用并启动
import { registerMicroApps, start } from "qiankun";

registerMicroApps([
  {
    name: "react",
    entry: "//localhost:8000",
    container: "#subapp",
    activeRule: "/react",
  },
  {
    name: "static",
    entry: "//localhost:9000",
    container: "#subapp",
    activeRule: "/static",
  },
]);

start();

静态微应用

  1. 创建静态页面
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>

    <script>
      const rootEle = document.getElementById("root");
      rootEle.innerHTML = `<div>this is static page</div>`;
    </script>
  </body>
</html>
  1. 导出生命周期钩子
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>

    <script>
      function render(props) {
        const { container } = props;
        const rootEle = container
          ? container.querySelector("#root")
          : document.getElementById("root");
        rootEle.innerHTML = `<div>this is static page</div>`;
      }
      if (!window.__POWERED_BY_QIANKUN__) {
        render({});
      }

      window.subStatic = {
        bootstrap: async () => {
          console.log("bootstrap");
        },
        mount: async (props) => {
          console.log("mount");
          render(props);
        },
        unmount: async () => {
          console.log("mount");
        },
      };
    </script>
  </body>
</html>

切到微应用路由后流程:

  • 主应用加载入口文件内容
  • 加工html字符串,(去掉外链css, 外链js)
  • 调用bootstrap方法
  • 将加工后的html字符串嵌入挂载点
  • 执行微应用js代码
  • 调用mount方法

从微应用路由切到其它路由流程:

  • 将挂载点的DOM移除
  • 调用unmount方法
  1. 启动:支持跨域serve -C

react 微应用

  1. 修改端口号:添加.env文件,修改端口号PORT=8000
  2. 打包成umd格式:安装修改配置的第三方工具@rescripts/cli,修改配置
    packaga.json
  "scripts": {
    "start": "rescripts start",
    "build": "rescripts build",
    "test": "rescripts test",
    "eject": "rescripts eject"
  }

.rescriptsrc.js

module.exports = {
  webpack: (config) => {
    config.output.library = "subReact";
    config.output.libraryTarget = "umd";
    return config;
  },
  devServer: (config) => {
    config.headers = {
      "Access-Control-Allow-Origin": "*",
    };
    return config;
  },
};

  1. 修改入口文件,导出生命周期钩子
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";

function render(props) {
  const { container } = props;
  const root = ReactDOM.createRoot(
    container
      ? container.querySelector("#root")
      : document.getElementById("root")
  );
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
}

if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}

export async function bootstrap(props) {
  console.log("bootstrap", props);
}
export async function mount(props) {
  console.log("mount", props);
  render(props);
}
export async function unmount(props) {
  console.log("unmount", props);
}

  1. 修改__webpack_public_path__
    新建 public-path.js并在入口文件首行导入
if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

  1. css沙箱
start({ sandbox: { experimentalStyleIsolation: true } });

微应用的样式添加了唯一的父选择器div[data-qiankun="react"]用这种方式隔离。

  1. 通信
    通过props通信
registerMicroApps([
  {
    name: "react",
    entry: "//localhost:8000",
    container: "#subapp",
    activeRule: "/react",
+    props: { key: 1, key2: 2 },
  },
  {
    name: "static",
    entry: "//localhost:5000",
    container: "#subapp",
    activeRule: "/static",
  },
]);
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容