# micro-frontend-top
**Repository Path**: wangrongding/micro-frontend-top
## Basic Information
- **Project Name**: micro-frontend-top
- **Description**: 基于qiankun的微前端落地实践~
- **Primary Language**: JavaScript
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2021-10-20
- **Last Updated**: 2025-01-08
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# qiankun 微前端架构从 0-1
## 项目说明
### 目录结构
- main 所有应用的基座,vue 构建
- packages 存放所有子应用
- script 项目需要的一些 Node 脚本
### 启动步骤
1. `yarn clone`clone 所有的子应用到 packages 目录中
2. `yarn ins`安装所有项目的依赖
3. `yarn start`启动所有服务
## 实现方法记录
### clone 子应用
本来是想通过`download-git-repo`来实现的,但是发现 clone 下来的仓库都没有.git 文件夹了,通过查看`download-git-repo`的源码发现里面有一行代码通过`rimraf`把.git 文件给删除了,并且没有可选项控制不删除,这样就不满足子应用需要独立的版本管理需求了.

所以我自己通过`download-git-repo`也在用的`git-clone`依赖,实现了一个符合当前需求的小工具
```js
const gitClone = require("git-clone/promise");
const loading = require("loading-cli");
/*
repoUrl:git仓库地址
targetPath:下载的目标目录
cloneOptions:git clone的可选参数
git:git二进制路径;默认:(git预计在你的$PATH)
shallow: when true,克隆深度为 1
checkout: 克隆后要检出的修订版/分支/标签
args: 要传递给的额外参数数组 git clone
eg: clone(repo, targetPath, [options]
*/
async function clone(repoUrl, targetPath, cloneOptions = { shallow: 1 }) {
if (!(repoUrl && targetPath)) {
console.log("repoUrl,targetPath为必传项!");
return;
}
const load = loading("请耐心等待,各应用正在clone...").start();
await gitClone(repoUrl, targetPath, cloneOptions)
.then(() => {
// console.clear();
console.log(`成功! clone ${targetPath}完成~ ✔💚`);
})
.catch((err) => {
console.log(
`clone :${targetPath} 出错! ,❌${err},请检查该文件夹是否已存在,或立即重试!`
);
})
.finally(() => {
load.stop();
});
}
```
可以看到有加载的状态
并且成功与失败的报错还是很友好的~

### npm 运行多命令
通过`npm-run-all`来实现
### 改造子应用
子应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。
**\*\*\*** **主要需要做如下配置(这里以 vue 应用为例):** 👈
- 在 子应用的 src 目录新增 public-path.js 文件
通过`__webpack_public_path__`设置 webpack publicPath,防止资源加载出错
```javascript
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
```
- 修改子应用中的 router 文件
将 src 下的 router/index.js 中的蓝色区域注释调,直接通过 `export default` 导出定义的路由数组
- 在子应用 src 下的 main.js 中
引入上面新增的`public-path.js`,然后新建一个`render`函数,并创建 VueRouter,然后挂载到应用上。
导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。
完整代码如下:
```javascript
import Vue from "vue";
import App from "./App.vue";
import store from "./store";
Vue.config.productionTip = false;
//-------------------------挂载应用------------------------------
import "./public-path";
import routes from "./router";
import VueRouter from "vue-router";
let Router = null;
let instance = null;
function render(props = {}) {
const { container, routerBase } = props;
//在 render 中创建 VueRouter,可以保证在卸载微应用时,移除 location 事件监听,防止事件污染
Router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? routerBase : "/",
mode: "history",
routes: routes,
});
// 挂载应用
instance = new Vue({
router: Router,
store,
render: (h) => h(App),
}).$mount(container ? container.querySelector("#app") : "#app"); //为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围
}
//---------------------------独立运行时-------------------------
//
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
//------------------------导出相应的生命周期钩子------------------
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log("react app bootstraped");
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log("VueMicroApp mount", props);
render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount(props) {
console.log("VueMicroApp unmount");
instance.$destroy();
instance.$el.innerHTML = "";
instance = null;
Router = null;
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
console.log("update props", props);
}
```
qiankun 是基于 single-spa 实现的,所以你可以在[Registered application lifecycle](https://single-spa.js.org/docs/building-applications/#registered-application-lifecycle)找到更多关于微应用生命周期相关的文档说明。
- 子应用的`vue.config.js`中必须添加如下配置
```js
const appName = require("./package.json").name;
module.exports = {
devServer: {
headers: {
"Access-Control-Allow-Origin": "*", // 主应用获取子应用时跨域响应头
},
},
configureWebpack: {
//为了让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置
output: {
library: `${appName}-[name]`, // 微应用的包名,必须与主应用中注册的微应用名称一样!
libraryTarget: "umd", // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${appName}`, //按需加载
},
},
};
```
### 基座应用需要做的事
- 在基座应用中安装 qiankun,`yarn add qiankun`/`xxx install qiankun`
- 在基座应用的 src 中新建一个 subApp.js 的文件,用于存放子应用的配置
具体如下
```js
const subApps = [
{
name: "sub-a",
entry: "//localhost:9425/",
activeRule: "/sub-a",
container: "#subapp-container", // 子应用挂载的 div
props: {
routerBase: "/sub-a", // 下发路由给子应用,子应用根据该值去定义 qiankun 环境下的路由
},
},
{
name: "sub-b",
entry: "//localhost:9426/",
activeRule: "/sub-b",
container: "#subapp-container",
props: {
routerBase: "/sub-b",
},
},
];
export default subApps;
```
在 基座应用的 App.vue 中设置好子应用挂载的节点
```html