【微前端乾坤】 vue2主应用、vue2+webpack子应用,vue3+webpack子应用、vue3+vite子应用的配置

因公司需求 需要将原本vue2+iframe 形式的项目改成微前端乾坤的方式。

之前iframe都是直接嵌套到vue2项目的二级目录或者三级目录下的(反正就是要随处可嵌)

用乾坤的原因:

1、iframe嵌套的方式存在安全隐患;

2、项目是联合开发的, 乾坤的方便多团队开发

3、乾坤方便后续增加子应用继续开发

注意使用场景:原本vue2+iframe 形式的项目改成微前端乾坤的方式,将vue2改成微服务的主应用(基座),之前iframe嵌套在vue2项目中的其他页面的系统改成微服务的子应用,嵌套的只是子应用路由中的一个页面 , 也就是 主应用 的一个 路由页面 对应 子应用 的一个 路由页面 , 不是那种常规的一个出口包含了整个子应用,一下主应用的配置主要是针对我这种需求弄的, 子应用大部分都是通用的

一、乾坤介绍

1、简介

简单:无论你使用什么技术栈,都可以轻松地将子应用接入到主应用中,就像使用 iframe 一样简单。
完备:qiankun 提供了丰富的功能,包括样式隔离、沙箱、预加载等,为开发者解决微前端架构中的各种问题。
生产可用:qiankun 已经在蚂蚁金服内部和外部的 200+ 应用中得到验证,是一个值得信赖的生产可用解决方案。

2、qiankun 的设计理念

qiankun 的设计理念主要体现在两个方面:技术栈无关和技术栈无关。技术栈无关:qiankun 不限制子应用的技术栈,无论是 React、Vue 还是 Angular,都可以轻松地接入到主应用中。这样可以消除应用之间的隐性依赖,实现真正的技术栈无关。
接入简单:qiankun 的目标是让接入微前端的过程就像使用 iframe 一样简单,从而尽可能减少对旧应用的改造工作量。

3、qiankun 的技术实现与选择

qiankun 在技术实现上主要解决了两个问题:应用的加载与切换和应用的隔离与通信。应用的加载与切换:qiankun 通过使用 single-spa,实现了应用的懒加载、路由处理和应用入口选择等功能。
应用的隔离与通信:qiankun 使用 JS 沙箱和样式隔离技术,确保子应用之间的 JS 和 CSS 不会发生冲突。同时,qiankun 提供了父子应用和子子应用之间的通信机制,实现了应用之间的数据传递和交互。

二、项目改造

我的项目目录格式

1、主应用(base

1)、安装乾坤

yarn add qiankun -S / npm i qiankun -S 

2)、在需要嵌套子应用页面的页面(主应用指定菜单下,之前iframe页面的地方)添加如下内容:在文件中准备子应用出口容器, 然后script中引入loadMicroApp方法加载子应用

html 复制代码
<!--
 * @Description: ------------ fileDescription -----------
 * @Author: snows_l snows_l@163.com
 * @Date: 2023-09-13 18:02:20
 * @LastEditors: snows_l snows_l@163.com
 * @LastEditTime: 2024-02-27 12:19:54
 * @FilePath: /digital-qiankun-common/base/src/views/machineRoom/index.vue
-->

<template>
  <div id="microAppContainer"></div>
</template>

<script>
import { loadMicroApp } from "qiankun";

export default {
  data() {
    return {
      microApp: null,
    };
  },

  mounted() {
    this.microApp = loadMicroApp(
      {
        name: "cmdb",
        entry: "//localhost:8991/",  // 子应用部署的服务地址、这里因为都是本地开所以是localhost,到时候部署到那个服务器就是那个服务器地址
        container: "#microAppContainer",
        // props 传参
        props: {
          token: sessionStorage.getItem("token"),
          localLogin: sessionStorage.getItem("local_login"),
          roleNamesStr: sessionStorage.getItem("roleNamesStr"),
        },
      },
      {
        // 是否开启沙箱样式隔离
        sandbox: {
          // experimentalStyleIsolation: false, // 沙箱隔离
          // strictStyleIsolation: false,  // 样式隔离(处于实验阶段), 是导致主应用的样子错乱, 用的话请检查主应用的样式是否被影响
        },
        // 是否为单实例场景,单实例指的是同一时间只会渲染一个微应用。默认为 false。
        singular: true,
      }
    );
  },

  updated() {
    if (this.microApp) {
      this.microApp.update();
    }
  },

  beforeDestroy() {
    if (this.microApp) {
      this.microApp.unmount();
    }
  },
};
</script>

3)、路由:由于是主应用与子应用的页面是多对多, 主应用需要包含菜单,头部logo、头像等title组件, 所以需要包含在layout组件内, 因此主应用与子应用的路由需要一样(相当于主应用与子应用使用同一个路由),路由的component都指定同一个vue文件,因为只有子应用的出口就只有这个一个文件。如下

javascript 复制代码
// 主应用:
{
  path: "/operations/machineRoom",
  redirect: { name: "MachineRoom" },
  component: Layout,
  meta: { title: "多云现场设备管理", icon: "资源展示" },
  children: [
    {
      path: "machineRoom",
      name: "MachineRoom",
      component: () => import("@/views/machineRoom/index"), // 同一个页面
    },
    {
      path: "hardware",
      name: "Hardware",
      component: () => import("@/views/machineRoom/index"), // 同一个页面
    },
  ],
},


// 子应用路由:
{
  name: 'resource',
  path: '/operations/machineRoom/:code',
  component: () => import('@/pages/resource/index')
}

这样主应用就搭建好了, 给子应用留了出口

2、vue 2 子应用

我这里子应用用的histor模式的路由, 子应用用的都是

1)、在src目录下添加public-path.js文件, 并在main.js入口文件中引入, public-path.js的内容如下:

javascript 复制代码
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

2)、改造mian.j入口文件,如下:

在入口文件中添加乾坤子应用的渲染方式, 并导出乾坤使用的三个生命周期钩子函数,改造成如下:----- 根据自己应用参考配置

javascript 复制代码
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import './public-path';
import routes from './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__ ? '/#/' : '/', //  如果在乾坤环境, 由于主应用是hash模式的路由所以需要加/#/, 独立运行,子应用是history, 所以是/
    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;
}

3)、路由:主应用的路由于微应用的路由保持一直(主应用中已经提到)

4)、在webpack.config.js 或者 vue.config.js中添加如下内容:

javascript 复制代码
const { name } = require("./package");
devServer: {
  port: 8031,
  headers: {
    "Access-Control-Allow-Origin": "*", 
  },
},

configureWebpack: {
  output: {
    library: `${name}-[name]`,
    libraryTarget: "umd", // 把微应用打包成 umd 库格式
    // webpack5 使用chunkLoadingGlobal: `webpackJsonp_${packageName}`,
    jsonpFunction: `webpackJsonp_${packageName}`,
  },
},

5)、打包,需要打包成umd库格式,配置如下

webpack5打包 添加如下内容:
javascript 复制代码
const packageName = require('./package.json').name;
module.exports = {
  output: {
    library: `${packageName}-[name]`,
    libraryTarget: 'umd',
    chunkLoadingGlobal: `webpackJsonp_${packageName}`,
  },
};
webpack4打包 添加如下内容
javascript 复制代码
const packageName = require('./package.json').name;

module.exports = {
  output: {
    library: `${packageName}-[name]`,
    libraryTarget: 'umd',
    jsonpFunction: `webpackJsonp_${packageName}`,
  },
};

3、vue 3 子应用(webpack打包)

1)、在入口文件中添加乾坤子应用的渲染方式, 并导出乾坤使用的三个生命周期钩子函数,改造成如下:----- 根据自己应用参考配置

javascript 复制代码
/*
 * @Description: ------------ fileDescription -----------
 * @Author: snows_l snows_l@163.com
 * @Date: 2023-09-26 09:05:04
 * @LastEditors: snows_l snows_l@163.com
 * @LastEditTime: 2024-03-01 16:04:40
 * @FilePath: /digital-qiankun-common/cmdb_vue/src/main.js
 */
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/reset.css';
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import './public-path';
import routers from './routers/index.js';
import myPinia from './store';


let app = null;
let router = null;

const render = container => {
  app = createApp(App);
  // 如果是在主应用的环境下就挂载主应用的节点,否则挂载到本地
  router = createRouter({
    history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? '/#/' : '/'),
    routes: routers
  });

  const appDom = container ? container.querySelector('#app') : '#app';
  app.use(router);
  app.use(Antd);
  app.use(myPinia);
  app.mount(appDom);
};


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


export async function bootstrap() {
  console.log('[vue] vue app bootstraped');
}

export async function mount(props) {
  render(props.container);
}

export async function unmount() {
  app.unmount();
  app = null;
  router = null;
}

2)、在main.js同级目录添加public-path.js文件, 并在main.js中引入

javascript 复制代码
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

3)、在vue.config.js的server添加如下内容

javascript 复制代码
headers: {
  'Access-Control-Allow-Origin': '*' // 主应用获取子应用时跨域响应头
}

4)、固定端口,主应用中会用到(开发环境用,发布的时候用的子应用部署的服务器地址)

4、vue 3 子应用(vite)

1)、安装vite的乾坤插件

javascript 复制代码
yarn add vite-plugin-qiankun -D / npm i vite-plugin-qiankun -D

2)、在入口文件中添加乾坤子应用的渲染方式, 并导出乾坤使用的三个生命周期钩子函数,改造成如下:----- 根据自己应用参考配置

javascript 复制代码
/*
 * @Description: ------------ fileDescription -----------
 * @Author: snows_l snows_l@163.com
 * @Date: 2023-09-26 09:05:04
 * @LastEditors: snows_l snows_l@163.com
 * @LastEditTime: 2024-02-27 10:36:31
 * @FilePath: /digital-qiankun-common/CMDB/src/main.js
 */

import { qiankunWindow, renderWithQiankun } from 'vite-plugin-qiankun/dist/helper';
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import routers from './routers/index.js';

import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/reset.css';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import zhCn from 'element-plus/dist/locale/zh-cn.mjs';
import myPinia from './store';

let app = null;
let router = null;

const render = container => {
  app = createApp(App);
  // 如果是在主应用的环境下就挂载主应用的节点,否则挂载到本地
  router = createRouter({
    history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/#/' : '/'),
    routes: routers // `routes: routes` 的缩写
  });

  const appDom = container ? container.querySelector('#app') : '#app';
  app.use(router);
  app.use(Antd);
  app.use(ElementPlus, {
    locale: zhCn
  });
  app.use(myPinia);
  app.mount(appDom);
};


if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render(null);
} else {
  renderWithQiankun({
    // bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap
    // 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等
    bootstrap() {},
    // 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法,也可以接受主应用传来的参数

    mount(_props) {
      console.log('-------- 子应用 mount --------', _props);
      _props.localLogin && sessionStorage.setItem('localLogin', _props.localLogin);
      _props.roleNamesStr && sessionStorage.setItem('roleNamesStr', _props.roleNamesStr);
      _props.token && sessionStorage.setItem('token', _props.token);
      render(_props.container);
    },

    // 应用每次 切出/卸载 会调用的unmount方法,通常在这里我们会卸载微应用的应用实例
    unmount(_propsy) {
      app.unmount();
      app = null;
      router = null;
    },

    update: function (_props) {
      render(_props.container);
    }
  });
}

3)、在vite.config.js中plugins中配置乾坤插件以及其他配置,参考配置如下:

javascript 复制代码
import qiankun from 'vite-plugin-qiankun';

base:'/',// 静态资源访问路径。 发布到线上的时候需要改成线上服务器完整的地址, 不然访问主应用的域名导致拿不到资源
plugins: [
  vue(),
  qiankun('cmdb', {
    useDevMode: true
  })
],

server:{
  headers: {
    'Access-Control-Allow-Origin': '*' // 主应用获取子应用时跨域响应头
  },
  origin: 'http://localhost:8991/' 
}

4)、打包

javascript 复制代码
// 修改在vite.config.js
base:'http://localhost:8991' // 应用部署的服务器地址

// 在output中添加format: 'umd',打包成库格式
build:{
  rollupOptions:{
    output:{
      format: 'umd'
    }
  }
}

三、觉得重要的地方提醒一下

1、子应用推荐 history 模式的路由;

2、只有主应用需要安装乾坤;

3、注意本地开发与部署到线上的子应用的地址;

4、子应用需要导出bootstrap、mount、unmount钩子函数工乾坤调用, 并在mount中拿到container去渲染子应用;

5、子应用必须打包成 umd 格式;

6、子应用部署的时候需要开启允许跨域访问,配置如下:

javascript 复制代码
# 允许跨域请求的域, * 表示所有
add_header 'Access-Control-Allow-Origin' *;

# 允许携带Cookie
add_header 'Access-Control-Allow-Credentials' 'true';

# 允许请求的方式 比如常用的Restful GET/PUT/POST/DELETE
add_header 'Access-Control-Allow-Methods' *;

# 允许请求的header
add_header 'Access-Control-Allow-Headers' *;

7、乾坤导出三个api,registerMicroApps, start以及loadMicroApp, registerMicroApps, start是搭配使用的, 使用registerMicroApps, start的时候需要添加activeRule去匹配路由(当匹配到activeRule值开头的路由的时候就会自动加载子应用), loadMicroApp是单独使用的, 不需要activeRule去匹配路由的

相关推荐
m0_748236119 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo61722 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_7482489423 分钟前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_7482356135 分钟前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink6 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-8 小时前
验证码机制
前端·后端
燃先生._.9 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js