微前端-qiankun

前言

微前端类似于微服务,实质上是将一个前端应用变为由多个独立子应用集合而成的应用。
其中每一个子应用都可以独立开发,独立部署,并且对技术栈没有要求。
目前个人使用的微前端框架是qiankun和wujie。
qiankun是蚂蚁金服基于Single SPA开发的一个微前端实现库。
wujie是一款基于 Web Components + iframe 微前端框架。
这里先记录一下qiankun的使用.
qiankun官网


一、主应用

主应用负责管理子应用页面的跳转

1.安装qiankun框架

npm i qiankun

2.路由配置

import { createRouter, createWebHistory } from 'vue-router';
const routes = [];
const router = createRouter({
  history: createWebHistory('/'),
  routes,
});
export default router

3.在main.js中注册路由和子应用

import { createApp } from 'vue'
import App from './App.vue'
import { registerMicroApps, start } from 'qiankun';
import router from '@/router/index'

registerMicroApps([
  {
    name: 'test-1',				//子应用1,全新的空白应用,vue2+js
    props: {},					//给子应用传递数据
    entry: '//localhost:8082',	//子应用入口
    container: '#main',			//子应用容器
    activeRule: '/test-1',		//子应用激活规则,主应用通过该字段判断是否切换子应用
  },
  {
    name: 'vite-project',		//子应用2,已经写过的demo,vue3+ts+vite
    props: {},
    entry: '//localhost:8085',
    container: '#main',
    activeRule: '/vite-project',
  },
]);
start();  //启动
createApp(App).use(router).mount('#main')

4.在App.vue中添加两个跳转按钮

<!--
 * @Description: 主应用界面
-->
<template>
  <div id="main">
    <div class="bens">
      //主应用跳转按钮
      <button type="primary" @click="$router.push('/test-1/app')">test-1</button>
      <button type="primary" @click="$router.push('/vite-project/Home')">vite-project</button>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "App",
  components: {},
  mounted() {},
};
</script>

<style>
#main {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  height: 100%;
  width: 100%;
}
#__qiankun_microapp_wrapper_for_vite_project__ {
  height: 100%;
  width: 100%;
}
</style>

在这里插入图片描述

二、子应用test-1

1.index.html添加容器id,需与在主应用注册时的container一致

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please
        enable it to continue.</strong
      >
    </noscript>
    //添加id
    <div id="test-1"></div>
  </body>
</html>

2.在src目录下添加public-path.js文件

if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

3.在main.js中注册生命周期并引入public-path.js文件

import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import './public-path' // 引入public-path.js

let instance = null;
// 限制子应用id查找范围,防止微应用的根 id 与其他 DOM 冲突
function render(props = {}) {
  if (instance) return;
  const { container } = props;
  instance = createApp(App)
    .use(router)
    .mount(container ? container.querySelector("#test-1") : "#test-1");
}

// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

//仅在初始化时调用一次
export async function bootstrap() {}

//每次进入子应用都会调用
export async function mount(props) {
  render(props);
}

//切出/卸载时调用
export async function unmount() {
  instance.$destroy?.();
  instance = null;
}

4.修改vue.config.js文件

const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package.json');
module.exports = defineConfig({
  publicPath: '/',
  transpileDependencies: true,
  configureWebpack: {
    plugins: [],
    output: {
      // 把子应用打包成 umd 库格式
      library: `${name}-[name]`,
      libraryTarget: 'umd',
      chunkLoadingGlobal: `webpackJsonp_${name}`,
    },
  },
  devServer: {
    port: 8082,
    headers: {
      //解决主应用跳转子应用出现的跨域问题
      'Access-Control-Allow-Origin': '*'
    },
    proxy: {
      '/dev-api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: p => p.replace(/^\/dev-api/, '')
      }
    }
  }
})

5.修改路由

import { createRouter, createWebHistory } from 'vue-router';
const routes = [
  {
    path: '/app',
    name: 'app',
    component: () => import('@/views/app.vue'),
  },
];
// 1.返回一个 router 实列,为函数,里面有配置项(对象) history
const router = createRouter({
  //基础路径需与主应用中注册的激活规则保持一致
  history: createWebHistory('/test-1'),
  routes,
});
export default router


三、子应用vite-project

基本步骤与另一个子应用一致,部分需要修改
该子应用使用的是vite,而qiankun目前不支持vite,需要使用vite-plugin-qiankun插件

1.安装并使用vite-plugin-qiankun

npm i vite-plugin-qiankun

该插件需在vite.config.ts配置,具体配置最后一起展示

2.index.html添加容器id(同test-1)

3.修改main.ts

import { createApp } from 'vue';
import App from './App.vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import Vue from 'vue';
import router from './router';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';

const app = createApp(App);

let instance = null as any;
function render(props = {}) {
  if (instance) return;
  const { container } = props as any;
  const app = createApp(App);
  instance = app
    .use(ElementPlus, { zIndex: 3000 })
    .use(router)
    .mount(container ? container.querySelector('#vite-project') : '#vite-project');
}

// 独立运行时
if (!(window as any).__POWERED_BY_QIANKUN__) {
}
export const bootstrap = () => {
  console.log('[vue] vue app bootstraped');
};
export async function mount(_props: any) {
  console.log('mount', _props);
  render(_props.container);
}
export async function unmount() {
  //可选链操作符
  instance.$destroy?.();
  instance = null;
}
export async function update() {}

const initQianKun = () => {
  // QiankunLifeCycle
  renderWithQiankun({
    mount,
    bootstrap,
    unmount,
    update
  });
};

qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render();

4.不需要public-path.js

网上查询时说vite也需要pablic-path.js文件,实际上我尝试时删了也没影响

5.修改vite.config.ts

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import commonjs from '@rollup/plugin-commonjs';
import requireTransform from 'vite-plugin-require-transform';
import path from 'path';
//qiankun vite插件
import qiankun from 'vite-plugin-qiankun';
export default defineConfig({
  // publicDir: 'http://localhost:8085',
  // base: 'http://localhost:8085',
  plugins: [
    commonjs() as any,
    vue(),
    qiankun('vue3', { useDevMode: true }),
    requireTransform({
      fileRegex: /.js$|.vue$/
    })
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src') // 别名
    }
  },
  optimizeDeps: {
    include: ['@/../lib/vform/designer.umd.js'] //此处路径必须跟main.js中import路径完全一致!
  },
  build: {
    /* 其他build生产打包配置省略 */
    //...
    commonjsOptions: {
      include: /node_modules|lib/ //这里记得把lib目录加进来,否则生产打包会报错!!
    },
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  },
  server: {
    port: 8085,
    headers: {
      'Access-Control-Allow-Origin': '*'
    },
    //解决静态资源报错
    origin: 'http://localhost:8085',
    proxy: {
      '/dev-api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: p => p.replace(/^\/dev-api/, '')
      }
    }
  }
});

6.修改路由(同test-1)

结果

8081是主应用端口,当qiankun监听到 test-1 时就会跳转到对应子应用
所有子应用都通过子应用间接调用,会出现静态资源404等问题也是因为在主应用找不到子应用资源

在这里插入图片描述
在这里插入图片描述

总结

实际上还是有些麻烦,最近尝试了一下wujie确实要简单很多,只是没有在具体项目中使用可能会遇到未知问题,我当前项目还是用的qiankun,之后看有没有时间再写一篇wujie的。
出现问题可以去官网找,其次是官网github的讨论区。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值