小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
导读
最近欣喜地发现,微前端,准确的说是 Qiankun,可以解决困扰了笔者很久的一个问题:如何平滑、彻底、稳定的重构项目。尤其是那种只有一两个页面,但非常复杂的应用(如谷歌地图),之前一直没有很好的解决方法。Qiankun 提供的“多实例”功能就可以很好的解决这个问题。
虽然 Qiankun 的初衷是想解决“巨石应用”的问题,但是很多事情的道理是相通的。本文会聚焦关注其在项目重构方面能够起到的作用,希望能够给大家提供一些新思路。
背景
团队处于 Vue2 到 Vue3 + TS 的过渡期,还希望尝试如 webpack4 到 vite、webpack5,eslint 与 prettier 优化等技术迭代。这种技术的升级往往会造成项目中新老技术同时存在一段时间,这会造成一定的冗余,而且老技术栈经常会成为重构过程中的掣肘,很别扭。
更严重的是,如果有 break change 那就要求一次性修改完全部,这样做一是工期长,二是一旦遗漏了某些修改,会对项目的稳定性造成很大影响。
必须承认,项目技术栈的更新升级是有价值且必要的。这样可以享受到技术栈自身升级带来的新特性,从而提高生产力。更重要的是,随着时间推移,生态中的工具都会转为支持新版技术栈,这时如果项目的技术栈落后太多,就很难再找到与之匹配的第三方库,只能自己开发,这对于生产效率来说绝对是一个很大的损失。
所以笔者一直在寻找一种能够平滑、稳定过渡的重构方法。曾经采取过的办法是:
- 新代码单独一个 repo,起一个服务,新路由,全部使用新技术栈,没有任何包袱;
- 老代码以“页面”(路由)为单位,逐页切换到新代码项目;
- 二者之间用“链接跳转”的方式切换。
这样做可以做到新老技术栈的完全隔离与自治。但缺点也很明显,两个项目互相跳转时会有白屏,在体验上会有较大损失(以极致的标准要求的话)。另外,对于那种只有一个页面,但是非常复杂的项目,这种用路由隔离的办法是无能为力的。
而 Qiankun2 似乎可以很好的解决这个问题。
目标
- 用“单实例”模式,解决可被路由级拆分的项目重构问题;
- 用“多实例”模式,解决不可被路由级拆分的,复杂单页面项目的重构问题;
- 输出 Demo 代码。
正文
注:请先浏览下 Qiankun 官网的项目实践。
准备
- 用
create-react-app创建项目 A,加上react-router,作为主项目; - 用
@vue/cli创建 Vue2 项目 B,作为“单实例”微应用; - 用
@vue/cli创建 Vue3 + TS 项目 C,作为“多实例”微应用;
主应用改造
yarn add qiankun安装 qiankun- 单实例 - 在
public/index.html中增加<div id="vue2Container"></div>,作为项目 B 的 container; - 多实例 - 新建
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;
- 修改入口文件
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 文件。
注意:微应用中
router的base设置,一定要与主应用中注册时的activeRule相同,本例中均为app-vue。后面多实例微应用改造也要注意同样的问题。
多实例微应用改造
具体操作参考《【微前端】Qiankun Vue3 配置》的微应用部分。
截图展示
为了节省空间,全部模拟移动端截的图。另外,可以发现此时项目的图片和懒加载的组件是有问题的,这一点有时间的话后续再撰文解决。
React 主应用首页
展示的是主应用自己的页面和组件。截图:
Vue 2 单实例
当前地址:http://localhost:3000/app-vue
展示的是微应用的页面。注意,顶部可以看到还留有主应用的导航,这是因为主应用中导航代码在 Switch 组件之外(可再看一下上面的代码),所以还会保留导航。截图:
Vue 3 多实例
当前地址:http://localhost:3000/micro-app/
展示的是主应用的 MicroApp 组件,Vue 3 的微应用嵌入其中,其实特别像 iframe。截图:
结语
从接入成本来看,代码层面成本不大,也许更大的工作量是在工程层面。目前的 Demo 尝试的还比较浅,但是已经让笔者看到了明显的价值,个人是很乐意在项目重构的实践中应用 Qiankun 的。
其实这里面还有个小坑,那就是“微应用如何跳转回主应用,或者直接跳转到其他微应用”。一些同学可能还没反应过来这个问题,没关系,慢慢想,先卖个关子。其实细心的同学应该能在 MicroApp.js 中寻找到一些蛛丝马迹。后面有时间再另起一文专门写吧。
最后,送给大家一句最近深有感触的一句话,与本文关系不大:
“完美不在于无以复加,而在于无可删减,万事莫不如此。”—— Antoine de St.Exupery