# qiankun-demo
**Repository Path**: zixuanmoon/qiankun-demo
## Basic Information
- **Project Name**: qiankun-demo
- **Description**: 基于qiankun微前端实践,主应用为vue,微应用为vue、react
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2022-08-24
- **Last Updated**: 2023-04-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# qiankun-demo
### 介绍
基于qiankun微前端实践,主应用vue,微应用vue、react
### 项目启动顺序
1. qiankun-base: npm run serve
2. qiankun-vue: npm run serve
3. qiankun-react: npm run start
启动顺序不一致可能会有端口问题,可取qiankun-base中修改main.js子应用端口设置
### qiankun介绍
https://qiankun.umijs.org/zh/guide
1. 特性:
- 技术栈无关
- 样式隔离: 确保微应用之间样式互相不干扰 shadow dom
- JS 沙箱 :确保微应用之间 全局变量/事件 不冲突
2. why not iframe
https://www.yuque.com/kuitos/gky7yw/gesexv
- iframe提供 浏览器原生的硬隔离方案, 不论是样式隔离、js 隔离这类问题统统都能被完美解决。 但他的最大问题也在于他的隔离性无法被突破,导致==应用间上下文无法被共享==,随之带来的开发体验、产品体验的问题。
- url 不同步
- UI不同步 DOM 结构不共享
- 全局上下文完全隔离 ,内存变量不共享
- 慢 : 每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程

### 入门案例
#### 主应用配置
##### 安装qiankun
##### 注册微任务registerMicroApps
1. 创建 qiankun-base (vue2 + webpack) *//vue-cli -->webpack*
2. 在主应用 安装qiankun
```vue
$ yarn add qiankun # 或者 npm i qiankun -S
```
3. 修改main.js 入口文件 ,注册微任务
```vue
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import { registerMicroApps, start } from "qiankun";// 1. 导入
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
//2. 注册微应用
registerMicroApps([
{
name: "vueApp",
entry: "//localhost:8080",
container: "#container",
activeRule: "/app-vue",
},
{
name: 'reactApp',
entry: 'http://localhost:3000', // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch
container: '#container',
activeRule: '/app-react',
}
]);
//3. 启动 qiankun
start();
```
说明:
当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。
4. 修改App.vue ,在主应用页面放置跳转链接以及 微应用对应的 container
```vue
```
#### vue微应用配置
> 微应用分为有 `webpack` 构建和无 `webpack` 构建项目,有 `webpack` 的微应用(主要是指 Vue、React、Angular) 需要做的事情有: ==用webpack构建==
>
> 1. 新增 `public-path.js` 文件,用于修改运行时的 `publicPath`。[什么是运行时的 publicPath ?](https://webpack.docschina.org/guides/public-path/#on-the-fly)。
>
> 注意:运行时的 publicPath 和构建时的 publicPath 是不同的,两者不能等价替代。
>
> 2. 微应用建议使用 `history` 模式的路由,需要设置路由 `base`,值和它的 `activeRule` 是一样的。
> 3. 在入口文件最顶部引入 `public-path.js`,修改并导出三个生命周期函数。
> 4. 修改 `webpack` 打包,允许开发环境跨域和 `umd` 打包。
>
> 主要的修改就是以上四个,可能会根据项目的不同情况而改变。例如,你的项目是 `index.html` 和其他的所有文件分开部署的,说明你们已经将构建时的 `publicPath` 设置为了完整路径,则不用修改运行时的 `publicPath` (第一步操作可省)。
>
> 无 `webpack` 构建的微应用直接将 `lifecycles` 挂载到 `window` 上即可。
##### 设置public-path
##### 设置history路由 base
##### 导出三个生命周期函数
##### 修改打包配置文件 vue.config.js 允许跨域和开启umd
1. 使用vue-cli 生成 vue 2.x 项目
2. 在src 目录新增==public-path.js==:
```vue
// 运行在qiankun时 修改运行时publicpath
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
```
3. 安装 vue-router
```vue
//vue 2.x --> vue-router 3.x
yarn install @3
//vue 3.x --> vue-router 4.x
yarn install
```
3. 修改 main.js 入口文件 导出子应用的生命周期:bootstarp 、mount 、 unmount
```vue
//1. 导入 public-path.js
import "./public-path";
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import routes from "./router";
// import store from "./store";
Vue.config.productionTip = false;
let router = null;
let instance = null;
function render(props = {}) {
//2. 微应用 使用history 模式路由 设置路由base, 值与activeRule 一致
const { container } = props;
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/',
mode: "history",
routes,
});
//3. 避免根id #app 与其他的DOM冲突 需要限制查找范围
instance = new Vue({
router,
// store,
render: (h) => h(App),
}).$mount(container ? container.querySelector("#app") : "#app");
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
// 4. 导出 三个生命周期函数
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log("[vue] vue app bootstraped");
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log("[vue] props from main framework", props);
render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = "";
instance = null;
router = null;
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
}
```
4. 修改打包配置 vue.config.js
```vue
// const { name } = require("./package");
module.exports = {
devServer: {
headers: {
//1. 允许开发环境跨域
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `vueApp-[name]`,
libraryTarget: "umd", // 2. 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_vueApp`,
// chunkLoadingGlobal: `webpackJsonp_${name}`,
},
},
};
```
5. app.vue
```vue
```
6. router/index.js
```vue
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/about",
name: "About",
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
];
export default routes;
```
#### react微应用配置
1. 安装create-react-app 脚手架
2. 创建react 项目
3. 安装路由 `react-router-dom`
```vue
npm install react-router-dom
```
4. 在src 目录新增 public-path.js
```vue
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
```
5. 修改index.js 入口文件
```vue
//1. 导入public-path
import "./public-path"
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router} from 'react-router-dom'
import './index.css';
import App from './App';
// import reportWebVitals from './reportWebVitals';
// if (window.__POWERED_BY_QIANKUN__) {
// // eslint-disable-next-line no-undef
// __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
// }
function render(props) {
const { container } = props;
ReactDOM.render(
//// 需要用BrowserRouter包裹app 设置 history 模式路由的 base
,
container ? container.querySelector('#root') : document.querySelector('#root')
);
}
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
// 导出三个生命周期
export async function bootstrap() {
console.log('[react16] react app bootstraped');
}
export async function mount(props) {
console.log('[react16] props from main framework', props);
render(props);
}
export async function unmount(props) {
const { container } = props;
ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}
```