微前端qiankun架构 (基于vue2实现)使用教程
Posted 不羁的程序员~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微前端qiankun架构 (基于vue2实现)使用教程相关的知识,希望对你有一定的参考价值。
工具使用版本
- node --> 16+
- @vue/cli --> 5+
创建文件
创建文件夹qiankun-test。
使用vue脚手架创建主应用main和子应用dev
主应用
安装 qiankun:
yarn add qiankun
或者
npm i qiankun -S
使用qiankun:
-
在 utils 内创建 微应用文件夹 microApp,在该文件夹内创建微应用出口文件 index.js,路由文件 microAppRouter,配置函数文件 microAppSetting。
-
路由文件 microAppRouter
// 微应用路由 const microAppRouter = [ name: "dev", //用于应用名 容器id 应用路由基地址 url: "//localhost:8080", //应用路径(ip与端口) props: propsName: "8080" , //初始化时需要传递给微应用的数据 // hidden: false,//是否启用该应用,默认false menuName: "dev",//自定义属性 根据需要自己配置(用在了菜单导航的名称) , ]; export default microAppRouter;
-
配置函数文件 microAppSetting
// 引入路由 import microAppRouter from "./microAppRouter"; // 微应用配置 const microAppSetting = ; export default microAppSetting; /** * @description: 配置子应用 * @param * * @return * */ microAppSetting.microApps = () => let apps = []; microAppRouter.map((item) => if (!item.hidden) apps.push( name: item.name, //应用名(不可重复) entry: item.url, //默认加载应用路径(ip与端口) container: `#$item.name`, //容器id activeRule: `/$item.name`, //激活该应用的路径hash模式+#(子应用路由基地址) ...item, ); ); return apps; ;
-
微应用出口文件 index.js
// 引入 qiankun 应用注册函数 开启函数 import registerMicroApps, start from "qiankun"; // 引入 微应用配置文件 import microAppSetting from "./microAppSetting"; //注册子应用 registerMicroApps(microAppSetting.microApps()); //开启 start(sandbox: strictStyleIsolation?: boolean, // 开启严格的样式隔离模式。这种模式下 qiankun 会为每个微应用的容器包裹上一个 [shadow dom]节点,从而确保微应用的样式不会对全局造成影响。 experimentalStyleIsolation?: boolean // 设置实验性的样式隔离特性,即在子应用下面的样式都会包一个特殊的选择器规则来限定其影响范围 );
-
在App.vue内配置微应用容器及跳转菜单
<template> <div id="fapp"> <!-- 主应用路由出口 --> <router-link to="/mian">主应用</router-link> <router-link v-for="(item,index) in microAppDom_Router" :key="index" :to="`/$item.name`?$item.props?.propsName" > item.menuName </router-link> <router-view></router-view> </div> </template> <script> // 引入子应用路由 import microAppRouter from "@/utils/microApp/microAppRouter"; export default name: 'App', data() return microAppDom_Router: microAppRouter, ; </script>
- 在main.js文件内引入微应用出口文件 index.js
import "@/utils/microApp/index";
7. 路由文件router.js
const router = new VueRouter(
mode: "history",
routes
)
子应用配置
- 在src中增加public-path.js文件
//public-path.js if (window.__POWERED_BY_QIANKUN__) __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
- 在main.js文件内导出生命周期钩子
import Vue from 'vue' import App from './App.vue' import './public-path' Vue.config.productionTip = false let instance = null; function render(props = ) const container = props; // Vue.use(router) instance = new Vue( render: (h) => h(App), ).$mount(container ? container.querySelector('#app') : '#app'); // 独立运行时 if (!window.__POWERED_BY_QIANKUN__) render(); export async function bootstrap() console.log('[vue] vue app bootstraped'); 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;
-
配置Webpack、跨域与端口号
const defineConfig = require('@vue/cli-service') const name = require('./package'); module.exports = defineConfig( devServer: headers: 'Access-Control-Allow-Origin': '*', , , configureWebpack: output: library: `$name-[name]`, libraryTarget: 'umd', // 把微应用打包成 umd 库格式 //jsonpFunction: `webpackJsonp_$name` // webpack5废弃jsonpFunction chunkLoadingGlobal: `webpackJsonp_$name`, , , );
- 路由文件router.js
const router = new VueRouter(
mode: "history",
base: "/dev",
routes,
);
报 ____webpack_public_path__未定义的问题
解决:
根据创建项目时选择的配置,在package.json文件内添加全局配置
module.exports =
---
globals:
__webpack_public_path__: "writable",
,
---
;
报子应用接口404问题
解决:
主应用vue.config.js配置代理
module.exports = defineConfig(
---
devServer:
proxy:
'/api1': // 匹配所有以'/api1' 开头的请求路径
target: 'http://localhost:8080/', // 代理目标的基础路径
changeOrigin: true,
pathRewrite: '^/api1':''
,
,
---
)
微前端架构-qiankun在vue3的应用
本文章介绍了qiankun在vue3的应用,其中子应用有vue2、vue3、react、angular
介绍
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
其他几款([single-spa]、[micro-app]、[百度emp]])
使用 iframe 整合系统时,假设我们有系统 A, 当我们想把系统 B 引入 A 系统时,只需要 B 系统提供一个 url 给 A 系统引用即可,这里我们把 A 系统叫做父应用,把 B 系统叫做子应用。同样的,微前端也延续了这个概念,微前端在使用起来基本和使用 iframe 一样平滑。
结构
主应用(父),微应用(子)
案例
一、主应用
- 主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 start 即可。
创建主应用项目 -vue3
npm install @vue/cli -g
vue create qiankun-tast
- 在主应用中安装qiankun框架
$ yarn add qiankun # 或者 npm i qiankun -S
- 在 主应用 中注册微应用
main.js:
import createApp from 'vue'
import App from './App.vue'
import router from './router/index'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'zone.js';
import registerMicroApps from 'qiankun';
registerMicroApps([
//
// name: "vue2App",
// props: age: 10 , //给子应用传数据
// entry: "//localhost:3001", //默认会加载这个html,解析里面的js,动态执行(子应用必须支持跨域)里面,是用fetch去请求的数据
// container: "#out-main", //挂载到主应用的哪个元素下
// activeRule: "/vue2", //当我劫持到路由地址为/vue2时,我就把http://localhost:3000这个应用挂载到#app-main的元素下
// ,
name: "vueChildOne",
entry: "//localhost:3001",
container: "#child-vue3-one-content",
activeRule: "/child-one",
,
name: "vueChildTwo",
entry: "//localhost:3002",
container: "#child-vue3-two-content",
activeRule: "/child-two",
,
name: "vue2Child",
entry: "//localhost:3003",
container: "#child-vue2-one-content",
activeRule: "/child-vue2-one",
,
name: "reactApp1",
entry: "//localhost:4001",
container: "#child-react-one-content",
activeRule: "/child-react-one",
,
name: "angularApp1",
entry: "//localhost:4200",
container: "#child-angular-one-content",
activeRule: "/child-angular-one",
,
]);
// setDefaultMountApp('/child-one')
// 启动 qiankun
// start();
createApp(App).use(ElementPlus).use(router).mount('#app-base')
App.vue
<template>
<div class="common-layout">
<el-container>
<el-aside width="200px">
<el-menu>
<el-menu-item index="1">
<el-icon><icon-menu /></el-icon>
<span @click="goHome">首页</span>
</el-menu-item>
<el-menu-item index="2">
<el-icon><icon-menu /></el-icon>
<span @click="$router.push('/child-one')">child-vue3-one</span>
</el-menu-item>
<el-menu-item index="3">
<el-icon><document /></el-icon>
<span @click="$router.push('/child-two')">child-vue3-one</span>
</el-menu-item>
<el-menu-item index="4">
<el-icon><document /></el-icon>
<span @click="$router.push('/child-vue2-one')">child-vue2-one</span>
</el-menu-item>
<el-menu-item index="5">
<el-icon><document /></el-icon>
<span @click="$router.push('/child-react-one')">child-react-one</span>
</el-menu-item>
<el-menu-item index="6">
<el-icon><document /></el-icon>
<span @click="$router.push('/child-angular-one')">child-angular-one</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-main> <router-view></router-view></el-main>
</el-container>
</div>
</template>
<script>
export default
name: "App",
components: ,
methods:
// 跳转页面方法
goHome()
this.$router.push("/");
,
,
;
</script>
<style>
.bens
width: 100%;
display: flex;
justify-content: center;
position: absolute;
top: 15px;
left: 0;
z-index: 9999999;
#app-base
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
</style>
index.html:
// 将id:app 改为 app-base 自定义就行,只要与main.js对应起来,切不与微应用重复
<div id="app-base"></div>
router.js
import createRouter, createWebHistory from "vue-router";
// 2. 配置路由
const routes = [
path: "/",
name: "home",
component: () => import("@/views/home/index.vue"),
,
path: "/child-one",
component: () => import("@/views/childOne/index.vue"),
,
path: "/child-two",
component: () => import("@/views/childTwo/index.vue"),
,
path: "/child-vue2-one",
component: () => import("@/views/childVue2One/index.vue"),
,
path: "/child-react-one",
component: () => import("@/views/childReactOne/index.vue"),
,
path: "/child-angular-one",
component: () => import("@/views/childAgOne/index.vue"),
,
];
// 1.返回一个 router 实列,为函数,里面有配置项(对象) history
const router = createRouter(
mode: 'history',
history: createWebHistory(),
routes,
);
// 3导出路由 然后去 main.ts 注册 router.ts
export default router
vue3子应用
- 创建项目
// 选择vue3这个版本
vue create child-one
-
在 src 目录新增 public-path.js
-
解决静态文件跨域
// src/public-path.js
if(window.__POWERED_BY_QIANKUN__)
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
- 修改路由文件,建议使用history 模式的路由,并设置路由 base,值和它的 activeRule 是一样的。
import createRouter, createWebHashHistory, createWebHistory from "vue-router";
// 2. 配置路由
const routes = [
path: '/',
component: () => import('@/views/home/index.vue'),
,
path: '/about',
component: () => import('@/views/about/index.vue'),
,
];
// 1.返回一个 router 实列,为函数,里面有配置项(对象) history
const router = createRouter(
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? "/child-one" : "/",
history: createWebHashHistory('/child-one'),
routes,
);
// 3导出路由 然后去 main.ts 注册 router.ts
export default router
- 入口文件 main.js 修改,为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围。并导出三个生命周期函数。
import createApp from 'vue'
import App from './App.vue'
import router from './router/index'
import './public-path'
// createApp(App).mount('#app')
let instance = null;
function render(props = )
if (instance) return;
const container = props;
console.log(container);
instance = createApp(App)
.use(router)
.mount(container ? container.querySelector("#app-child-one") : "#app-child-one");
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__)
render();
export async function bootstrap()
console.log("[vue] vue app bootstraped");
export async function mount(props)
console.log("[vue] props from main framework", props);
render(props);
export async function unmount()
//可选链操作符
instance.$destroy?.();
instance = null;
- 主应用容器子应用
qiankun-test/src/views/childOne/index.vue
<template>
<h2>我是子应用 vue3-one</h2>
<div id="child-vue3-one-content"></div>
</template>
<script>
import start from "qiankun";
export default
name: "childOne",
components: ,
mounted()
if (!window.qiankunStarted)
window.qiankunStarted = true;
start();
,
;
</script>
<style>
</style>
运行效果如下:
vue2子应用-child-vue2
childVue2One/index.vue
<template>
<h2>我是微应用vue2项目</h2>
<div id="child-vue2-one-content"></div>
</template>
<script>
import start from "qiankun";
export default
name: "vueChild",
components: ,
mounted()
this.$nextTick(() =>
if (!window.qiankunStarted)
window.qiankunStarted = true;
start();
);
,
;
</script>
<style>
</style>
- 微应用配置child-vue2
src下创建public-path.js
if (window.__POWERED_BY_QIANKUN__)
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
main.js
// src/main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import "./public-path";
Vue.config.productionTip = false
// 定义一个Vue实例
let instance = null
// 渲染方法
function render(props = )
const container = props
instance = new Vue(
router,
render: (h) => h(App)
).$mount(container ? container.querySelector('#app'): '#app')
// 独立运行时
if(!window.__POWERED_BY_QIANKUN__)
render()
//暴露主应用生命周期钩子
/**
* bootstrap : 在微应用初始化的时候调用一次,之后的生命周期里不再调用
*/
export async function bootstrap()
console.log('vue2-app bootstraped');
/**
* mount : 在应用每次进入时调用
*/
export async function mount(props)
console.log('vue2-app mount', props);
render(props);
/**
* unmount :应用每次 切出/卸载 均会调用
*/
export async function unmount()
console.log("vue2-app unmount")
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
vue.config.js
module.exports =
lintOnSave: false,
devServer:
port: "3003",
headers:
"Access-Control-Allow-Origin": "*", //所有人都可以访问我的服务器
,
,
configureWebpack:
output:
// library: `$name-[name]`,
library: `vueChildOne`,
libraryTarget: "umd", // 把微应用打包成 umd 库格式
// jsonpFunction: `webpackJsonp_$name`,
,
,
;
router.js
import createRouter, createWebHashHistory, createWebHistory from "vue-router";
// 2. 配置路由
const routes = [
path: '/',
component: () => import('@/views/home/index.vue'),
,
path: '/about',
component: () => import('@/views/about/index.vue'),
,
];
// 1.返回一个 router 实列,为函数,里面有配置项(对象) history
const router = createRouter(
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? "/child-one" : "/",
history: createWebHashHistory('/child-one'),
routes,
);
// 3导出路由 然后去 main.ts 注册 router.ts
export default router
vue2错误问题
路由版本不对
下载指定版本在3*的就行
react子应用
问题
- 当修改入口文件index.tsx之后,主要是添加了qiankun的生命周期之后,报错
– You need to export lifecycle functions in reactApp1 entry
明明我已经写了生命周期但是没有生效。
问题出在:官方问题使用的js语法,我使用的是ts语法。
解决:用react-app-rewired方案复写webpack就可以了。作用:通过react-app-rewired插件,react-app-rewired的作用就是在不eject的情况下,覆盖create-react-app的配置.
angular子应用
angular由于在国内用的不多所以我是按照官方教程完成的,当然中间出了很多狗血的错误
官方:以 Angular-cli 9 生成的 angular 9 项目为例,其他版本的 angular 后续会逐渐补充。
这句话就是一个坑,首先我自己原有的angular版本是12,用 ng 命令安装的项目就是最新的了。这个导致我安装官方操作一直没有成功,不断报错。------我放弃了,做个乖孩子,用angular9
由于不能降低电脑全局版本,于是我在本项目中安装了一个angular-cli9
npm install @angular/cli@9.0.1
ng new child-angular1
版本搞成了9那就好办了
- 根据要求配置好主应用的main.js与App.vue文件
- 在主应用views创建anguale的容器.vue文件
- 配置主应用路由
- 然后就是根据qiankun的文档配置文件了
注意:在qiankun的文档中第二步,child-angular-one这个是和主应用配置路由一致
设置 history 模式路由的 base,src/app/app-routing.module.ts 文件:
import NgModule from '@angular/core';
import Routes, RouterModule from '@angular/router';
import APP_BASE_HREF from '@angular/common';
const routes: Routes = [];
@NgModule(
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
// @ts-ignore
// child-angular-one 必须和主路由向对应
providers: [ provide: APP_BASE_HREF, useValue: window.__POWERED_BY_QIANKUN__ ? '/child-angular-one' : '/' ]
)
export class AppRoutingModule
gitee地址:qiankun-vue3
以上是关于微前端qiankun架构 (基于vue2实现)使用教程的主要内容,如果未能解决你的问题,请参考以下文章