Vue微前端,使用的一些重点,打个总结。
首先,看一下 qiankun 官网的介绍。
核心价值和设计理念,在此不做赘述。有兴趣自己慢慢看。我这篇文章,以项目实践为例,只说重点。
代码已经放到 Gitee上面。
版本信息
我使用的 node 版本是 v20.13.1
vue脚手架版本是 @vue/cli 5.0.8
vue-router 版本 3.4.9
创建主应用
vue create qiankun-main
在主应用中,新建组件
src\components\Main.vue
主应用的Main组件:
<template>
<div class="wrapper">我是主应用的页面</div>
</template>
<script>
export default {
name: "Main",
};
</script>
src\router\index.js 配置路由:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const routes = [
{
path: '/',
redirect: '/main'
},
{
path: '/main',
component: () => import('@/components/Main'),
}
]
// 防止连续点击多次路由报错
let routerPush = Router.prototype.push;
let routerReplace = Router.prototype.replace;
// push
Router.prototype.push = function push(location) {
return routerPush.call(this, location).catch(err => err)
}
// replace
Router.prototype.replace = function push(location) {
return routerReplace.call(this, location).catch(err => err)
}
export default new Router({
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes
})
main.js中引入路由:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
修改 vue.config.js 允许跨域访问子应用页面
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8086,
headers: {
// 允许跨域访问子应用页面
'Access-Control-Allow-Origin': '*',
}
}
})
修改 App.vue 组件
/qiankun-child 是微应用的名称,后面要用到。
id="child" 是子项目容器id 后面要用到。
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<router-link to="/">跳转主应用的页面</router-link>
<!-- /qiankun-child 是微应用的名称 后面要用到 -->
<span style="margin: 12px;"></span>
<router-link to="/qiankun-child">跳转子应用的页面</router-link>
<router-view />
<!-- id="child" 是子项目容器id 后面要用到 -->
<div id="child"></div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
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>
主应用安装 qiankun 注册微应用
详细介绍,参考官网,快速上手
安装 qiankun
npm i qiankun --save
修改 main.js 在主应用中,注册微应用
重点:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
import { registerMicroApps, start } from "qiankun"
// 在主应用中注册子应用
registerMicroApps([
{
name: "vue app",
entry: "//localhost:8087",
// 子项目容器id
container: '#child',
// 子项目的名称
activeRule: '/qiankun-child'
}]
);
// 启动
start();
创建子应用
vue create qiankun-child
新建 src\components\Child.vue 组件:
<template>
<div class="wrapper">我是子应用的页面</div>
</template>
<script>
export default {
name:"Child",
}
</script>
src\router\index.js 添加路由信息
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const routes = [
{
path: '/',
redirect: '/child'
},
{
path: '/child',
component: () => import('@/components/Child'),
}
]
export default routes
新建 src\public-path.js 文件:
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
然后,在main.js中引入 public-path.js 并且,进行微应用的相关配置:
重点:
base: window.__POWERED_BY_QIANKUN__ ? '/qiankun-child/' : '/',
import "./public-path.js"
import Vue from 'vue'
import App from './App.vue'
import routes from './router'
import VueRouter from 'vue-router'
Vue.config.productionTip = false
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/qiankun-child/' : '/',
mode: 'history',
routes,
});
instance = new Vue({
router,
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;
router = null;
}
修改 vue.config.js
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package.json')
module.exports = defineConfig({
devServer: {
port: 8087,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
// wepack配置
configureWebpack: {
// 子应用打包成 umd 库格式
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
// webpack v4:
// jsonpFunction: `webpackJsonp_${name}`,
// webpack v5: chunkLoadingGlobal
chunkLoadingGlobal: `webpackJsonp_${name}`,
},
},
})
子应用的 App.vue 组件
<template>
<div id="app">
子应用 qiankun-child
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
效果展示:
通过 loadMicroApp 加载微应用
主应用中,新增 src\components\TestLoadMicroApp.vue 组件:
<template>
<div>
<button @click="loadApp">挂载微应用</button>
<button @click="unloadApp">卸载微应用</button>
<!-- 提供挂载容器 -->
<div id="sub-app-container"></div>
</div>
</template>
<script>
import { loadMicroApp } from "qiankun";
export default {
data() {
return {
microApp: null, // 微应用实例
};
},
methods: {
loadApp() {
if (this.microApp) return;
this.microApp = loadMicroApp({
// 微应用注册名字相同
name: "qiankun-child",
// 微应用的入库路径
entry: "http://localhost:8087",
// 微应用挂载的容器
container: "#sub-app-container",
props: {
// 主应用向微应用传递参数
userInfo:{
name: "张三",
age: 18,
}
},
});
this.microApp.mountPromise.then(() => {
console.log("微应用加载完成");
});
},
unloadApp() {
if (!this.microApp) return;
this.microApp.unmount(); // 卸载微应用
this.microApp = null;
},
},
};
</script>
修改主应用中的 App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<router-link to="/">跳转主应用的页面</router-link>
<!-- /qiankun-child 是微应用的名称 后面要用到 -->
<span style="margin: 12px;"></span>
<router-link to="/qiankun-child">跳转子应用的页面</router-link>
<span style="margin: 12px;"></span>
<router-link to="/test">手动加载微应用</router-link>
<router-view />
<!-- id="child" 是子项目容器id 后面要用到 -->
<div id="child"></div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
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>
修改主应用中 src\router\index.js 的 routes
const routes = [
{
path: '/',
redirect: '/main'
},
{
path: '/main',
component: () => import('@/components/Main'),
},
{
path: '/test',
component: () => import('@/components/TestLoadMicroApp'),
}
]
修改子应用中的 src\router\index.js 的 routes
const routes = [
{
path: '/',
redirect: '/child'
},
{
path: '/child',
component: () => import('@/components/Child'),
},
{
path: '/test',
component: () => import('@/components/Child'),
}
]
注意,保持主应用和子应用中的 /test 路由名称一样。
修改子应用中的 main.js 里的 render 方法。在微应用入口文件接收到主应用传递的参数,挂载到Vue原型上面,方便子应用中的组件进行访问。
function render(props = {}) {
const { container } = props;
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/qiankun-child/' : '/',
mode: 'history',
routes,
});
instance = new Vue({
router,
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
// 接收主应用传递的props
const userInfo = props.userInfo;
console.log('在微应用入口文件接收到主应用传递的userInfo:', userInfo);
// 将userInfo挂载到Vue.prototype上,方便组件访问
Vue.prototype.$userInfo = userInfo;
}
效果展示:
以上,如果对你有用的话,不妨点赞收藏关注一下,谢谢 🙏
😊 微信公众号: OrzR3
💖 不定期更新一些技术类,生活类,读书类的文章。