微前端 qiankun

2023-6-23 931 6/23

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}`,
   },
 },
})

 

结果

微前端  qiankun微前端  qiankun微前端  qiankun

 

单独运行的微应用

微前端  qiankun微前端  qiankun

 

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

 

- THE END -
0

共有 0 条评论