この記事では、vue3 での qiankun のアプリケーションを紹介します。そのサブアプリケーションには、vue2、vue3、react、angular が含まれます。
導入
qiankunはシングル スパに基づくマイクロ フロントエンド実装ライブラリであり、本番対応のマイクロ フロントエンド アーキテクチャ システムをより簡単かつ簡単に構築できるようにすることを目的としています。
その他 ([ single-spa ]、[ micro-app ]、[ Baidu emp ]])
iframe を使用してシステムを統合する場合、システム A があるとします。システム B をシステム A にインポートする場合、システム A が参照する URL をシステム B に提供するだけで済みます。ここでは、システム A を親アプリケーション、システム B と呼びます。サブアプリ。同様に、マイクロフロントエンドもこのコンセプトを継承しており、マイクロフロントエンドの使用は基本的に iframe を使用するのと同じくらいスムーズです。
構造
メインアプリ(親)、マイクロアプリ(子)
ケース
1. 主な用途
- メイン アプリケーションはテクノロジ スタックに限定されません。コンテナ DOM を提供し、マイクロ アプリケーションを登録して起動するだけで済みます。
メイン アプリケーション プロジェクトを作成する - 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__;
}
- ルーティング ファイルを変更するには、履歴モード ルーティングを使用し、ルーティング ベースを設定することをお勧めします。値はその 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 との競合を避けるために、検索範囲を制限する必要があります。そして、3 つのライフサイクル関数をエクスポートします。
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 子アプリケーション-子-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;
}
view.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* でダウンロードするだけです
反応サブアプリ
質問
- エントリ ファイル index.tsx を変更した後、主に qiankun のライフサイクルを追加した後、エラーが報告される
– reactApp1 エントリでライフサイクル関数をエクスポートする必要があります
明らかに、私はライフサイクルを書きましたが、それは有効になっていません。
問題は、公式の質問で使用される js 構文です。私は ts 構文を使用します。
解決策: react-app-rewiredソリューションを使用して webpack を書き換えるだけです。機能: react-app-rewired プラグインを通じて、react-app-rewired の機能は、イジェクトせずに create-react-app の構成をオーバーライドすることです。
角度サブアプリ
Angularは中国ではあまり使われていないので、公式チュートリアルに従って完成させましたが、途中で血なまぐさいミスが多々ありました。
公式: Angular-cli 9 によって生成された angular 9 プロジェクトを例に取ります。他のバージョンの angular は後で徐々に追加されます。
この文は罠です まず、元のangularのバージョンは12で、ngコマンドでインストールしたプロジェクトは最新です。これにより、公式の操作のインストールに成功せず、エラーを報告し続けました。------私はあきらめて、良い子になり、angular9を使用します
コンピューターのグローバル バージョンを縮小できないため、このプロジェクトに angular-cli9 をインストールしました
npm install @angular/[email protected]
ng new child-angular1
バージョンが9になると楽になる
- 要件に従って、メイン アプリケーションの main.js および App.vue ファイルを構成します。
- メイン アプリケーション ビューで Anguale コンテナー .vue ファイルを作成します。
- メイン アプリケーション ルートを構成する
- 次に、qiankunのドキュメントに従ってファイルを構成します。
注: qiankun ドキュメントの 2 番目のステップでは、child-angular-one はメインのアプリケーション構成ルートと一致しています.
履歴モード ルートのベース、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