本文章介紹了qiankun在vue3的應(yīng)用,其中子應(yīng)用有vue2、vue3、react、angular
介紹
qiankun 是一個(gè)基于 single-spa 的微前端實(shí)現(xiàn)庫,旨在幫助大家能更簡(jiǎn)單、無痛的構(gòu)建一個(gè)生產(chǎn)可用微前端架構(gòu)系統(tǒng)。
其他幾款([single-spa]、[micro-app]、[百度emp]])
使用 iframe 整合系統(tǒng)時(shí),假設(shè)我們有系統(tǒng) A, 當(dāng)我們想把系統(tǒng) B 引入 A 系統(tǒng)時(shí),只需要 B 系統(tǒng)提供一個(gè) url 給 A 系統(tǒng)引用即可,這里我們把 A 系統(tǒng)叫做父應(yīng)用,把 B 系統(tǒng)叫做子應(yīng)用。同樣的,微前端也延續(xù)了這個(gè)概念,微前端在使用起來基本和使用 iframe 一樣平滑。
結(jié)構(gòu)
主應(yīng)用(父),微應(yīng)用(子)
案例
一、主應(yīng)用
- 主應(yīng)用不限技術(shù)棧,只需要提供一個(gè)容器 DOM,然后注冊(cè)微應(yīng)用并 start 即可。
創(chuàng)建主應(yīng)用項(xiàng)目 -vue3
npm install @vue/cli -g
vue create qiankun-tast
- 在主應(yīng)用中安裝qiankun框架
$ yarn add qiankun # 或者 npm i qiankun -S
- 在 主應(yīng)用 中注冊(cè)微應(yīng)用
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 }, //給子應(yīng)用傳數(shù)據(jù)
// entry: "http://localhost:3001", //默認(rèn)會(huì)加載這個(gè)html,解析里面的js,動(dòng)態(tài)執(zhí)行(子應(yīng)用必須支持跨域)里面,是用fetch去請(qǐng)求的數(shù)據(jù)
// container: "#out-main", //掛載到主應(yīng)用的哪個(gè)元素下
// activeRule: "/vue2", //當(dāng)我劫持到路由地址為/vue2時(shí),我就把http://localhost:3000這個(gè)應(yīng)用掛載到#app-main的元素下
// },
{
name: "vueChildOne",
entry: "http://localhost:3001",
container: "#child-vue3-one-content",
activeRule: "/child-one",
},
{
name: "vueChildTwo",
entry: "http://localhost:3002",
container: "#child-vue3-two-content",
activeRule: "/child-two",
},
{
name: "vue2Child",
entry: "http://localhost:3003",
container: "#child-vue2-one-content",
activeRule: "/child-vue2-one",
},
{
name: "reactApp1",
entry: "http://localhost:4001",
container: "#child-react-one-content",
activeRule: "/child-react-one",
},
{
name: "angularApp1",
entry: "http://localhost:4200",
container: "#child-angular-one-content",
activeRule: "/child-angular-one",
},
]);
// setDefaultMountApp('/child-one')
// 啟動(dòng) 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: {
// 跳轉(zhuǎn)頁面方法
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對(duì)應(yīng)起來,切不與微應(yīng)用重復(fù)
<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.返回一個(gè) router 實(shí)列,為函數(shù),里面有配置項(xiàng)(對(duì)象) history
const router = createRouter({
mode: 'history',
history: createWebHistory(),
routes,
});
// 3導(dǎo)出路由 然后去 main.ts 注冊(cè) router.ts
export default router
vue3子應(yīng)用
- 創(chuàng)建項(xiàng)目
// 選擇vue3這個(gè)版本
vue create child-one
-
在 src 目錄新增 public-path.js
-
解決靜態(tài)文件跨域
// src/public-path.js
if(window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
- 修改路由文件,建議使用history 模式的路由,并設(shè)置路由 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.返回一個(gè) router 實(shí)列,為函數(shù),里面有配置項(xiàng)(對(duì)象) history
const router = createRouter({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? "/child-one" : "/",
history: createWebHashHistory('/child-one'),
routes,
});
// 3導(dǎo)出路由 然后去 main.ts 注冊(cè) router.ts
export default router
- 入口文件 main.js 修改,為了避免根 id #app 與其他的 DOM 沖突,需要限制查找范圍。并導(dǎo)出三個(gè)生命周期函數(shù)。
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");
}
// 獨(dú)立運(yùn)行時(shí)
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;
}
- 主應(yīng)用容器子應(yīng)用
qiankun-test/src/views/childOne/index.vue
<template>
<h2>我是子應(yīng)用 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>
運(yùn)行效果如下:
vue2子應(yīng)用-child-vue2
childVue2One/index.vue
<template>
<h2>我是微應(yīng)用vue2項(xiàng)目</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>
- 微應(yīng)用配置child-vue2
src下創(chuàng)建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
// 定義一個(gè)Vue實(shí)例
let instance = null
// 渲染方法
function render(props = {}) {
const { container } = props
instance = new Vue({
router,
render: (h) => h(App)
}).$mount(container ? container.querySelector('#app'): '#app')
}
// 獨(dú)立運(yùn)行時(shí)
if(!window.__POWERED_BY_QIANKUN__) {
render()
}
//暴露主應(yīng)用生命周期鉤子
/**
* bootstrap : 在微應(yīng)用初始化的時(shí)候調(diào)用一次,之后的生命周期里不再調(diào)用
*/
export async function bootstrap() {
console.log('vue2-app bootstraped');
}
/**
* mount : 在應(yīng)用每次進(jìn)入時(shí)調(diào)用
*/
export async function mount(props) {
console.log('vue2-app mount', props);
render(props);
}
/**
* unmount :應(yīng)用每次 切出/卸載 均會(huì)調(diào)用
*/
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": "*", //所有人都可以訪問我的服務(wù)器
},
},
configureWebpack: {
output: {
// library: `${name}-[name]`,
library: `vueChildOne`,
libraryTarget: "umd", // 把微應(yīng)用打包成 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.返回一個(gè) router 實(shí)列,為函數(shù),里面有配置項(xiàng)(對(duì)象) history
const router = createRouter({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? "/child-one" : "/",
history: createWebHashHistory('/child-one'),
routes,
});
// 3導(dǎo)出路由 然后去 main.ts 注冊(cè) router.ts
export default router
vue2錯(cuò)誤問題
路由版本不對(duì)
下載指定版本在3*的就行
react子應(yīng)用
問題
- 當(dāng)修改入口文件index.tsx之后,主要是添加了qiankun的生命周期之后,報(bào)錯(cuò)
– You need to export lifecycle functions in reactApp1 entry
明明我已經(jīng)寫了生命周期但是沒有生效。
問題出在:官方問題使用的js語法,我使用的是ts語法。
解決:用react-app-rewired方案復(fù)寫webpack就可以了。作用:通過react-app-rewired插件,react-app-rewired的作用就是在不eject的情況下,覆蓋create-react-app的配置.
angular子應(yīng)用
angular由于在國內(nèi)用的不多所以我是按照官方教程完成的,當(dāng)然中間出了很多狗血的錯(cuò)誤
官方:以 Angular-cli 9 生成的 angular 9 項(xiàng)目為例,其他版本的 angular 后續(xù)會(huì)逐漸補(bǔ)充。
這句話就是一個(gè)坑,首先我自己原有的angular版本是12,用 ng 命令安裝的項(xiàng)目就是最新的了。這個(gè)導(dǎo)致我安裝官方操作一直沒有成功,不斷報(bào)錯(cuò)。------我放棄了,做個(gè)乖孩子,用angular9
由于不能降低電腦全局版本,于是我在本項(xiàng)目中安裝了一個(gè)angular-cli9
npm install @angular/cli@9.0.1
ng new child-angular1
版本搞成了9那就好辦了
- 根據(jù)要求配置好主應(yīng)用的main.js與App.vue文件
- 在主應(yīng)用views創(chuàng)建anguale的容器.vue文件
- 配置主應(yīng)用路由
- 然后就是根據(jù)qiankun的文檔配置文件了
注意:在qiankun的文檔中第二步,child-angular-one這個(gè)是和主應(yīng)用配置路由一致
設(shè)置 history 模式路由的 base,src/app/app-routing.module.ts 文件:文章來源:http://www.zghlxwxcb.cn/news/detail-404124.html
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 必須和主路由向?qū)?yīng)
providers: [{ provide: APP_BASE_HREF, useValue: window.__POWERED_BY_QIANKUN__ ? '/child-angular-one' : '/' }]
})
export class AppRoutingModule { }
gitee地址:qiankun-vue3文章來源地址http://www.zghlxwxcb.cn/news/detail-404124.html
到了這里,關(guān)于微前端架構(gòu)-qiankun在vue3的應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!