微前端qiankun接入Vue和React项目
主应用:Vue3+Webpack
1、创建主应用:
npx vue create main-vue3-app
2、安装qiankun
npx yarn add qiankun
3、项目中使用的vue、vue-router、qiankun依赖如下,webpack版本为5.x
4、在根目录下创建vue.config.js
const { defineConfig } = require('@vue/cli-service');
const packageName = require('./package.json').name;
module.exports = defineConfig({
lintOnSave: false,
devServer: {
// 可以在配置中 配置端口 VUE_APP_PORT = 8080
port: 8080,
headers: {
'Access-Control-Allow-Origin': '*' // 允许跨域访问子应用页面
}
}
})
5、入口组件App.vue
<template>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/vue2-webpack-app">wepback+vue2App</router-link> |
<router-link to="/vue3-vite-app">vite+vue3App</router-link> |
<router-link to="/vue3-webpack-app">wepback+vue3App</router-link> |
<router-link to="/react-webpack-app">wepback+reactApp</router-link> |
<router-link to="/react-vite-app">vite+reactApp</router-link>
</nav>
<router-view/>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
</style>
6、然后创建微应用对应的路由页面以及注册微应用
router.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'index',
component: () => import('../views/Index.vue')
},
{
path: '/vue2-webpack-app/:chapters*',
name: 'vue2-webpack-app',
component: () => import('../views/Vue2App.vue')
},
{
path: '/vue3-vite-app/:chapters*',
name: 'vue3-vite-app',
component: () => import('../views/ViteApp.vue')
},
{
path: '/vue3-webpack-app/:chapters*',
name: 'vue3-webpack-app',
component: () => import('../views/Vue3App.vue')
},
{
path: '/react-webpack-app/:chapters*',
name: 'react-webpack-app',
component: () => import('../views/ReactWebpack.vue')
},
{
path: '/react-vite-app/:chapters*',
name: 'react-vite-app',
component: () => import('../views/ReactVite.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
微应用:Vue2+Webpack
1、在主应用下创建对应的路由页面 views/Vue2App.vue
<template>
<div id="vue2-webpack-app"/>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { registerMicroApps, start } from 'qiankun';
const loading = ref(false);
registerMicroApps([
{
name: 'vue2-webpack-app',
entry: '//localhost:8081/',
container: '#vue2-webpack-app',
activeRule: '/vue2-webpack-app',
}
]);
onMounted(() => {
if (!window['qiankunStarted']) {
window['qiankunStarted'] = true;
start();
}
});
</script>
<style scoped></style>
2、创建vue2+webpack项目,选2.x版本
npx vue create vue2-app
3、项目中使用的vue、vue-router依赖如下,webpack版本为5.x
4、在根目录下创建vue.config.js
const { defineConfig } = require('@vue/cli-service');
const packageName = require('./package.json').name;
module.exports = defineConfig({
lintOnSave: false,
devServer: {
// 可以在配置中 配置端口 VUE_APP_PORT = 8080
port: 8081,
headers: {
'Access-Control-Allow-Origin': '*' // 允许跨域访问子应用页面
}
},
configureWebpack: {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
chunkLoadingGlobal: `webpackJsonp_${packageName}`,
},
}
})
5、在src根目录下创建public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
6、router/index.ts
import Vue from 'vue'
import VueRouter from 'vue-router'
import IndexVue from '../views/Index.vue'
Vue.use(VueRouter)
const routes= [
{
path: '/',
name: 'index',
component: IndexVue
// component: () => import( '@/views/Index.vue')
},
]
console.log(window.__POWERED_BY_QIANKUN__)
const router = new VueRouter({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? '/vue2-webpack-app' : '/',
routes
})
export default router
7.入口文件main.ts/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router';
import './public-path'
Vue.config.productionTip = false
let instance:any = null;
function render(props:any = {}) {
const { container } = props
instance = new Vue({
name: 'root',
router,
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log('vue2+webpack bootstraped');
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props:unknown) {
// ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
console.log('乾坤子应用容器加载完成,开始渲染 child')
console.log('props from main mount', props)
render(props)
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
instance?.$destroy();
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props:unknown) {
console.log('update props', props);
}
微应用:Vue3+Wepback
1、在主应用下创建对应的路由页面 views/Vue3App.vue
<template>
<div id="vue3-webpack-app" />
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { registerMicroApps, start } from 'qiankun';
const loading = ref(false);
registerMicroApps([
{
name: 'vue3-webpack-app',
entry: '//localhost:8082/',
container: '#vue3-webpack-app',
activeRule: '/vue3-webpack-app',
}
]);
onMounted(() => {
if (!window['qiankunStarted']) {
window['qiankunStarted'] = true;
start({
prefetch:false,
sandbox:{experimentalStyleIsolation:true}
});
}
});
</script>
<style scoped></style>
2、创建vue3+webpack项目,选3.x版本
npx vue create vue3-webpack-app
3、项目中使用的vue、vue-router依赖如下,webpack版本为5.x
4、在根目录下创建vue.config.js
const { defineConfig } = require('@vue/cli-service');
const packageName = require('./package.json').name;
const path = require('path');
module.exports = defineConfig({
lintOnSave: false,
devServer: {
// 可以在配置中 配置端口 VUE_APP_PORT = 8080
port: 8082,
headers: {
'Access-Control-Allow-Origin': '*' // 允许跨域访问子应用页面
}
},
configureWebpack: {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
chunkLoadingGlobal: `webpackJsonp_${packageName}`,
},
resolve:{
alias:{
'@':path.join(__dirname,'src')
}
}
}
})
5、在src根目录下创建public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
6、router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import IndexView from '../views/Index.vue'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'index',
component: IndexView
// component: () => import('../views/Index.vue')
}
]
const router = createRouter({
history: createWebHistory(window.__POWERED_BY_QIANKUN__?'/vue3-webpack-app':process.env.BASE_URL),
routes
})
export default router
7.入口文件main.ts/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './public-path'
let app: any = null
function render(props: any = {}) {
app = createApp(App)
const { container } = props
app.use(router).mount(container ? container.querySelector('#app') : '#app')
}
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
export async function bootstrap() {
console.log('vue3+webpack bootstraped');
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props: unknown) {
// ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
console.log('乾坤子应用容器加载完成,开始渲染 child')
console.log('props from main mount', props)
render(props)
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
console.log('unmount-------------------')
app.unmount()
app = null
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props: unknown) {
console.log('update props', props);
}
微应用:Vue3+Vite
1、在主应用下创建对应的路由页面 views/ViteApp.vue
<template>
<div id="vue3-vite-app" />
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { registerMicroApps, start } from 'qiankun';
const loading = ref(false);
registerMicroApps([
{
name: 'vue3-vite-app',
entry: '//localhost:5174/',
container: '#vue3-vite-app',
activeRule: '/vue3-vite-app',
}
]);
onMounted(() => {
if (!window['qiankunStarted']) {
window['qiankunStarted'] = true;
start();
}
});
</script>
<style scoped></style>
2、创建vue3+vite项目
npx pnpm create vite vue3-vite-app --template vue-ts
3、项目中使用的vue、vue-router依赖如下,vite版本为4.x
4、安装vite-plugin-qiankun插件
npx pnpm add vite-plugin-qiankun
在vite.config.ts使用
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue() ,qiankun('vue3-vite-app', {
useDevMode: true
})],
server:{
host: '127.0.0.1',
port: 5174,
}
})
5、router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import {
qiankunWindow
} from 'vite-plugin-qiankun/dist/helper';
const router = createRouter({
history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/vue3-vite-app' : '/'),
routes: [
{
path: '/',
name: 'index',
component: () => import(/* webpackChunkName: "index" */ '../views/Index.vue')
}
]
})
export default router
6、在src根目录下创建public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
7、main.ts
import { createApp } from 'vue'
import './public-path'
import App from './App.vue'
import router from './router'
import {
renderWithQiankun,
qiankunWindow
} from 'vite-plugin-qiankun/dist/helper';
let app:any;
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
createApp(App).use(router).mount('#app');
} else {
renderWithQiankun({
mount(props) {
console.log('--mount');
console.log(props)
app = createApp(App);
app
.use(router)
.mount(
(props.container
? props.container.querySelector('#app')
: document.getElementById('app')) as Element
);
},
bootstrap() {
console.log('--bootstrap');
},
update() {
console.log('--update');
},
unmount() {
console.log('--unmount');
app?.unmount();
}
});
}
微应用:React 18+Webpack
1、在主应用下创建对应的路由页面 views/ReactWebpack.vue
<template>
<div id="react-webpack-app"/>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { registerMicroApps, start } from 'qiankun';
const loading = ref(false);
registerMicroApps([
{
name: 'react-webpack-app',
entry: '//localhost:3000/',
container: '#react-webpack-app',
activeRule: '/react-webpack-app',
props: {
}
}
]);
onMounted(() => {
if (!window['qiankunStarted']) {
window['qiankunStarted'] = true;
start();
}
});
</script>
<style scoped></style>
2、创建React+Webpack项目
npx create-react-app react-app
3、项目中使用的react、react-dom、react-router-dom依赖如下,webpack版本为5.x
4、创建config-overrides.js,修改配置
npm i react-scripts
修改package.json
5、封装路由组件,src/router/index.js
import React from 'react'
import Home from '../views/home'
// 导入路由依赖
import { Route,Routes} from 'react-router-dom'
export default function Router(){
return (
// 使用BrowserRouter包裹,配置路由
<Routes >
<Route element={<Home/>} path='/'></Route>
</Routes>
)
}
6、在App.jsx中引入路由组件
import Router from './router/index'
import './App.css';
function App() {
return (
<div>
<Router></Router>
</div>
);
}
export default App;
7、index.js入口
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'
import './index.css';
import App from './App';
import './public-path';
import reportWebVitals from './reportWebVitals';
// let root = createRoot(document.querySelector('#root'))
let root = null;
function render (props) {
const { container } = props;
root = root || ReactDOM.createRoot(container ? container.querySelector("#root") : document.getElementById("root") );
root.render(
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? "/react-webpack-app" : "/"}>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
);
}
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
export async function bootstrap () {
console.log("[react18] react app bootstraped");
}
export async function mount (props) {
console.log("[react18] props from main framework", props);
render(props);
}
export async function unmount (props) {
root.unmount();
root = null;
}
reportWebVitals();
微应用:React18+Vite
1、在主应用下创建对应的路由页面 views/ReactVite.vue
<template>
<div id="react-vite-app"/>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { registerMicroApps, start } from 'qiankun';
const loading = ref(false);
registerMicroApps([
{
name: 'react-vite-app',
entry: '//localhost:5175/',
container: '#react-vite-app',
activeRule: '/react-vite-app',
}
]);
onMounted(() => {
if (!window['qiankunStarted']) {
window['qiankunStarted'] = true;
start();
}
});
</script>
<style scoped></style>
2、创建React18+vite项目
npx pnpm create vite react-vite-app --template react-ts
3、项目中使用的react、react-dom 、react-router-dom依赖如下,vite版本为4.x
4、安装vite-plugin-qiankun插件
npx pnpm add vite-plugin-qiankun
在vite.config.ts使用
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import qiankun from 'vite-plugin-qiankun'
export default defineConfig({
plugins: [
// 在开发模式下需要把react()关掉
// https://github.com/umijs/qiankun/issues/1257
// react(),
qiankun('react-vite-app', { // 微应用名字,与主应用注册的微应用名字保持一致
useDevMode: true
})
],
server: {
host: '127.0.0.1',
port: 5175,
}
})
5、封装路由组件,src/router/index.tsx
import React from 'react'
import Home from '../views/home'
// 导入路由依赖
import { Route,Routes} from 'react-router-dom'
export default function Router(){
return (
// 使用BrowserRouter包裹,配置路由
<Routes >
<Route element={<Home/>} path='/'></Route>
</Routes>
)
}
6、在App.tsx中引入路由组件
import Router from './router/index'
import React from 'react';
import './App.css';
function App() {
return (
<div>
<Router></Router>
</div>
);
}
export default App;
7、main.tsx入口
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { BrowserRouter } from 'react-router-dom'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
let root:any;
const render = (container:HTMLElement | undefined) => {
// 如果是在主应用的环境下就挂载主应用的节点,否则挂载到本地
root = root || ReactDOM.createRoot(container ? container.querySelector("#root") : document.getElementById("root") );
console.log(qiankunWindow.__POWERED_BY_QIANKUN__)
root.render(
<BrowserRouter basename={qiankunWindow.__POWERED_BY_QIANKUN__ ? "/react-vite-app" : "/"}>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
);
}
const initQianKun = () => {
renderWithQiankun({
// 当前应用在主应用中的生命周期
// 文档 https://qiankun.umijs.org/zh/guide/getting-started#
mount(props) {
render(props.container)
// 可以通过props读取主应用的参数:msg
// 监听主应用传值
props.onGlobalStateChange((res) => {
console.log(res.count)
})
},
bootstrap() { },
unmount() {
root.unmount();
root = null
},
})
}
// 判断当前应用是否在主应用中
qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render(undefined)
演示:
20231027-120328
issue
发现使用webpack构建的微应用,不支持路由懒加载的写法,vite就可以
Vue路由懒加载报错,ChunkLoadError: Loading chunk 0 failed,需要二次进入子应用或者在子应用中刷新才正常。 · Issue #1929 · umijs/qiankun (github.com)
完整代码:
UzumakiHan/qiankun-demo (github.com)