emmm,我觉得只能算是简单的理解吧,将就搭建个简单的demo。
只能说qiankun的官方文档实在是很抽象!
微前端可以实现各个前端应用「原子化」,可以独立运行、开发、部署,从而满足业务的快速变化,以及分布式、多团队并行开发的需求。而且与技术栈无关。qiankun的话是基于single-spa去实现的。
我觉得基座与底座的拆分是有两种思路的
第一种基座共享依赖,像请求类、公用布局组件(比如aside、header)都尽可能由基座提供。
第二种能够让子应用可以完全独立运行,以防某个子应用要独立出来使用的场景。
demo包含一个主应用,两个微应用(只需要主应用去安装qiankun的依赖)
主应用
main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './public-path'
import { createPinia } from 'pinia'
import { registerMicroApps, start, setDefaultMountApp ,initGlobalState} from 'qiankun'
import {useStore} from './store/piniaStore'
/**
* 路由的问题
* 静态资源的引入
* 沙箱
*/
//使用pinia进行组件通信
let app = null
function initVue() {
app = createApp(App)
app.use(router).use(createPinia())
app.mount('#app')
}
initVue()
registerMicroApps([
{
name: 'demovue3one', // 微应用的名称
entry: '//localhost:7800', // 微应用的 entry 地址
container: '#appOne', // 微应用的容器节点
activeRule: '/app1', // 微应用的激活规则
props:useStore()
},
{
name: 'demovue3Tow', // 微应用的名称
entry: '//localhost:7801', // 微应用的 entry 地址
container: '#appTow', // 微应用的容器节点
activeRule: '/app2', // 微应用的激活规则
}
])
start()
其实主要做的是在主应用中注册需要引入的微应用
在主应用和微应用中src目录下添加public-pathe.js文件
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
主要是为了子应用可以动态地修改资源请求的基础路径,使得资源的加载路径正确指向当前子应用的路径。
在我们使用到的地方直接通过Id去引入即可,ID对应的是在main.ts注册微应用的激活规则
我的demo中是这样使用的
<template>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>|
<router-link to="/app1/">marcoOne</router-link>|
<router-link to="/app2">marcoTow</router-link>|
</nav>
<div id="appOne"></div>
<div id="appTow"> </div>
<router-view />
</template>
<style lang="less">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: red;
}
nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
</style>
div为对应的微应用的载体。
微应用怎么去设置呢,微应用是需要去配置跨域的,因为我是开发环境,所以直接使用proxy反向代理即可,
需要在微应用中去注册乾坤对应的钩子函数来启动
/* eslint-disable */
// @ts-nocheck
import './public-path'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import store from './store'
import {useStore} from './store/piniaStore'
import 'vant/lib/index.css';
import vant from 'vant'
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
function render(props = {}) {
const { container,age1,name1 } = props;
// container真实dom 挂载点
createApp(App).use(store).use(router).use(createPinia()).use(vant).mount(container ? container.querySelector('#app') : '#app')
}
// 独立运行时,直接挂载应用
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
// const store=useStore()
export async function bootstrap() {
// console.log("VueMicroApp bootstraped");
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log("VueMicroApp mount进入", props);
console.log('window打印', (window as any).__POWERED_BY_QIANKUN__)
// useStore()
const {id1,name1}=props
// store.age=age1
// store.name1=name1
render(props);//在后进行实例注册
const store= useStore()
store.name1=name1
store.age=id1
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
console.log("VueMicroApp unmount卸载");
}
因为我还使用了pinia来进行基座与底座之间的通信的。不过这里是有个坑的,就是要用到或者去改变store中的数据应该是在render执行之后,就是挂载完毕之后。
另外底座的路由配置
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
const routes: Array<RouteRecordRaw> = [
{
path: "/",
name: "home",
component: () => import('../App.vue'),
redirect: '/index',
children: []
},
{
path: '/index',
name: 'index',
component: () => import('../views/index.vue')
},//主页面
{
path: '/identity',
name: 'identity',
component: () => import('../views/identity.vue')
},//身份认证的界面
{
path: '/login',
name: 'login',
component: () => import('../views/login.vue')
},//登陆
{
path: '/invoicing',
name: 'invoicing',
component: () => import('../views/recording/Invoicing.vue')
}//录单开票
];
//const url = (window as any).__POWERED_BY_QIANKUN__ ? '/app1' : ''//是否为子应用 为的话就加上前缀 /app1 不是的话就默认其""
const router = createRouter({
history: createWebHistory((window as any).__POWERED_BY_QIANKUN__ ? '/app1' : ''),
routes,
});
export default router;
另外需要去修改底座的一些基于webpack的配置,因为他要求的输出的文件格式是umd的格式,而且也需要去配置跨域
这是我底座的vue.config.ts的配置
const { defineConfig } = require('@vue/cli-service')
const packageName = require('./package.json').name;
module.exports = defineConfig({
lintOnSave: false,
transpileDependencies: true,
devServer: {
port: 7800,
//开启跨域
headers: {
'Access-Control-Allow-Origin': '*',
}
},
configureWebpack: {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
// jsonpFunction: `webpackJsonp_${packageName}`,
},
},
})
结果



单独运行的微应用


其实在真正的项目中的话这类技术是有比较多的坑的,我搭建的demo很简单,也只算简单的去了解点吧。也没有去解决样式污染,js污染等问题,不过确实对这方面还是有兴趣的
共有 0 条评论