【微前端】Qiankun 其实是一个项目重构的利器

1,809 阅读6分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

导读

最近欣喜地发现,微前端,准确的说是 Qiankun,可以解决困扰了笔者很久的一个问题:如何平滑、彻底、稳定的重构项目。尤其是那种只有一两个页面,但非常复杂的应用(如谷歌地图),之前一直没有很好的解决方法。Qiankun 提供的“多实例”功能就可以很好的解决这个问题。

虽然 Qiankun 的初衷是想解决“巨石应用”的问题,但是很多事情的道理是相通的。本文会聚焦关注其在项目重构方面能够起到的作用,希望能够给大家提供一些新思路。

背景

团队处于 Vue2Vue3 + TS 的过渡期,还希望尝试如 webpack4vitewebpack5eslintprettier 优化等技术迭代。这种技术的升级往往会造成项目中新老技术同时存在一段时间,这会造成一定的冗余,而且老技术栈经常会成为重构过程中的掣肘,很别扭。

更严重的是,如果有 break change 那就要求一次性修改完全部,这样做一是工期长,二是一旦遗漏了某些修改,会对项目的稳定性造成很大影响。

必须承认,项目技术栈的更新升级是有价值且必要的。这样可以享受到技术栈自身升级带来的新特性,从而提高生产力。更重要的是,随着时间推移,生态中的工具都会转为支持新版技术栈,这时如果项目的技术栈落后太多,就很难再找到与之匹配的第三方库,只能自己开发,这对于生产效率来说绝对是一个很大的损失。

所以笔者一直在寻找一种能够平滑、稳定过渡的重构方法。曾经采取过的办法是:

  1. 新代码单独一个 repo,起一个服务,新路由,全部使用新技术栈,没有任何包袱;
  2. 老代码以“页面”(路由)为单位,逐页切换到新代码项目;
  3. 二者之间用“链接跳转”的方式切换。

这样做可以做到新老技术栈的完全隔离与自治。但缺点也很明显,两个项目互相跳转时会有白屏,在体验上会有较大损失(以极致的标准要求的话)。另外,对于那种只有一个页面,但是非常复杂的项目,这种用路由隔离的办法是无能为力的。

Qiankun2 似乎可以很好的解决这个问题。

目标

  1. 用“单实例”模式,解决可被路由级拆分的项目重构问题;
  2. 用“多实例”模式,解决不可被路由级拆分的,复杂单页面项目的重构问题;
  3. 输出 Demo 代码。

正文

注:请先浏览下 Qiankun 官网的项目实践

准备

  1. create-react-app 创建项目 A,加上 react-router,作为主项目;
  2. @vue/cli 创建 Vue2 项目 B,作为“单实例”微应用;
  3. @vue/cli 创建 Vue3 + TS 项目 C,作为“多实例”微应用;

主应用改造

  1. yarn add qiankun 安装 qiankun
  2. 单实例 - 在 public/index.html 中增加 <div id="vue2Container"></div>,作为项目 B 的 container;
  3. 多实例 - 新建 MicroApp.js 组件(页面)作为项目 C 的载体,代码如下
import React, { useEffect, useRef } from "react";
import { useHistory } from "react-router-dom";
import { loadMicroApp } from "qiankun";

function App() {
  const microAppContainer = useRef(null);
  const history = useHistory();

  useEffect(() => {
    let mainApp;
    if (microAppContainer.current) {
      // 手动加载微应用
      mainApp = loadMicroApp({
        name: "vue3 ts app",
        entry: "//localhost:8081",
        container: microAppContainer.current,
        // 将路由注入子应用
        props: { mainAppRouter: history },
      });
    }
    return () => {
      // 别忘了卸载
      mainApp.unmount();
    };
  }, []);

  return (
    <div>
      <h1>This Page contains a micro app</h1>
      {/* 微应用容器 */}
      <div style={{ border: "1px solid #000" }} ref={microAppContainer}></div>
    </div>
  );
}

export default App;
  1. 修改入口文件 index.js,增加引用、路由、微应用注册等代码,如下
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import { registerMicroApps, start } from "qiankun";
import "./index.css";
import App from "./App";
import MicroApp from "./MicroApp";
import reportWebVitals from "./reportWebVitals";

// 注册微任务
registerMicroApps([
  {
    name: "vue2 app", // app name registered
    entry: "//localhost:8080",
    container: "#vue2Container",
    activeRule: "/app-vue",
  },
]);

start();

// 增加路由
ReactDOM.render(
  <React.StrictMode>
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            {/* 单实例微应用 Vue 2 */}
            <Link to="/app-vue">Vue 2</Link>
          </li>
          <li>
            {/* 多实例微应用 Vue 3 + TS */}
            <Link to="/micro-app">MicroApp</Link>
          </li>
        </ul>

        <hr />

        <Switch>
          <Route path="/" exact>
            <App />
          </Route>
          <Route path="/micro-app">
            <MicroApp />
          </Route>
        </Switch>
      </div>
    </Router>
  </React.StrictMode>,
  document.getElementById("root")
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

单实例微应用改造

具体操作参考《Qiankun 官网 - 指南/项目实践/微应用/vue-微应用》即可,可能需要自己新建 vue.config.js 文件。

注意:微应用中 routerbase 设置,一定要与主应用中注册时的 activeRule 相同,本例中均为 app-vue。后面多实例微应用改造也要注意同样的问题。

多实例微应用改造

具体操作参考《【微前端】Qiankun Vue3 配置》的微应用部分。

截图展示

为了节省空间,全部模拟移动端截的图。另外,可以发现此时项目的图片和懒加载的组件是有问题的,这一点有时间的话后续再撰文解决。

React 主应用首页

当前地址:http://localhost:3000/

展示的是主应用自己的页面和组件。截图:

image.png

Vue 2 单实例

当前地址:http://localhost:3000/app-vue

展示的是微应用的页面。注意,顶部可以看到还留有主应用的导航,这是因为主应用中导航代码在 Switch 组件之外(可再看一下上面的代码),所以还会保留导航。截图: image.png

Vue 3 多实例

当前地址:http://localhost:3000/micro-app/

展示的是主应用的 MicroApp 组件,Vue 3 的微应用嵌入其中,其实特别像 iframe。截图:

image.png

结语

从接入成本来看,代码层面成本不大,也许更大的工作量是在工程层面。目前的 Demo 尝试的还比较浅,但是已经让笔者看到了明显的价值,个人是很乐意在项目重构的实践中应用 Qiankun 的。

其实这里面还有个小坑,那就是“微应用如何跳转回主应用,或者直接跳转到其他微应用”。一些同学可能还没反应过来这个问题,没关系,慢慢想,先卖个关子。其实细心的同学应该能在 MicroApp.js 中寻找到一些蛛丝马迹。后面有时间再另起一文专门写吧。

最后,送给大家一句最近深有感触的一句话,与本文关系不大:

“完美不在于无以复加,而在于无可删减,万事莫不如此。”—— Antoine de St.Exupery