07. vue3+vite+qiankun搭建微应用前端框架,并接入vue3微应用

news2024/11/20 1:52:31

目录

  • 前言
  • 主应用
  • 微应用
  • 部署

前言

因为业务系统接入的需要,决定将一个vue3+vite+ts的主应用系统,改造成基于qiankun的微应用架构。此文记录了改造的过程及vue3微应用接入的种种问题。

网上有很多关于微应用改造的案例,但很多都没写部署之后什么情况。写了部署的,没有实操部署在二级目录、三级目录是什么情况,甚至没有对部署之后的情况做测试、没有说明。这是在整个改造过程中最难的一点,也是最困扰我的一个问题。我们所改造的应用说明:

  • 主应用:vue3+vite+ts
  • 微应用1:vue2,qiankun官网API是基于vue2+webpack,我们对vue2也进行了接入,但是在本篇文章中不做说明。
  • 微应用2:vue3+vite,由于主应用已经是vue3的系统,所以,微应用也决定直接使用vue3框架

不懂的地方可以参考qiankun官方API,本文记录的是 基于vue3+vite的微应用,如何在vue3+vite的主应用中接入,以及完整的改造过程。首先有三个前提条件:

  • 主应用为经典运营系统的左右布局结构(下图),要求将微应用接入为主应用的一个路由页面
  • 微应用和主应用部署在不同的服务;
  • 主应用部署在二级目录。

图中所示的微应用菜单处,就是可以动态插拔微应用的菜单。

请添加图片描述

主应用

  1. 在主应用中注册微应用路由
const microRouters = [
    {
        path: '/vite-vue3-app2/', // path值必须与微应用中,路由前缀的`${parentBase}${packagejson.name}`中的${packagejson.name}值相同,否则访问会出错,${parentBase}为主应用部署的目录路径,后面内容中会讲
        component: Layout,
        name: 'vite-vue3-app2',
        meta: { title: '微应用测试2', icon: 'dashboard' },
        children: [ // 想要显示的微应用路由,都需要在主应用中注册
            {
                path: 'hello',
                component: () => import('@/views/Portal.vue'), // 所有路由都使用同一个vue组件
                hidden: false,
                name: 'app2-hello',
                meta: { title: 'app2-hello', icon: 'dashboard' }
            },
            {
                path: 'home',
                component: () => import('@/views/Portal.vue'),
                hidden: false,
                name: 'app2-home',
                meta: { title: 'app2-home', icon: 'dashboard' }
            },
            {
                path: 'about',
                component: () => import('@/views/Portal.vue'),
                hidden: false,
                name: 'app2-about',
                meta: { title: 'app2-about', icon: 'dashboard' }
            },
        ]
    },
]
  1. 注册微应用
registerMicroApps([
  {
        name: 'app2',
        entry: '/app2/', // entry,访问时,主应用的路径中会带有/app2/,通过niginx代理进入微应用
        container: '#view-main',
        activeRule: location => {
            return location.pathname.includes('/vite-vue3-app2') // 路由中包含/vite-vue3-app时,激活该微应用
        },
        props: {
            agg: '/vite-vue3-app2',
            // 注意:_parent_base是主应用的路由前缀(可以使用变量),在微应用启动时会使用到(!!!重点)
            _parent_base: '/portal/admin-console/'
        }
    }

注册微应用阶段,需要注意的事项:

    1. entry使用一个字符串'/app2/',在服务配置中通过nginx代理转发到微应用;
    1. 注册微应用时,传递参数_parent_base,这是主应用部署的二级目录,在微应用不独立运行时,路由需要携带该参数一起。
    1. 父级路由的path: '/vite-vue3-app2/',必须与微应用中的路由前缀${parentBase}${packagejson.name}${packagejson.name}的值相同,否则访问会出错。(${parentBase}是主应用部署的目录路径,后面内容中会讲)
  1. 注册Portal.vue组件,并在组件 mounted 中调用start函数
<template>
    <div id="view-main" class="sub-app-container"></div>
</template>

<script lang="ts" setup>
import { onMounted } from 'vue';
import { start } from 'qiankun'

onMounted(() => {
    if(!(window as any).qiankunStarted) {
        (window as any).qiankunStarted = true
        start({ sandbox: { experimentalStyleIsolation: true, singular: false } });
    }
})
</script>

微应用

因为qiankun目前还没有支持vue3使用,官方API中的方法行不通,可以使用有大佬开发的vite-plugin-qiankun插件。首先在微应用中安装插件:vite-plugin-qiankun,然后按照如下方法修改:

1.main.js文件修改

import { createApp } from 'vue' // vue3引入
import Cookies from 'js-cookie'
import ElementPlus from 'element-plus' // 引入element-plus
import locale from 'element-plus/lib/locale/lang/zh-cn' // 中文语言

import '@/assets/styles/index.scss' // global css

import App from './App'
import store from './store' //store

/**
 * 这里需要特别注意:我们将router定义为了一个函数。
 * 因为主应用部署在二级目录,当微应用不独立运行时,如果微应用的router没有带上主应用二级目录作为前缀,访问会出错,具体原因下面介绍
 */
import { router } from './router' // router

import plugins from './plugins' // plugins
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'

let app = null
// 独立运行时
if(!qiankunWindow.__POWERED_BY_QIANKUN__) {
  app = createApp(App)
  app.use(router('')).use(store).use(plugins) // router(''),独立运行,路由前缀为空
  app.use(ElementPlus, {
    locale: locale,
    size: Cookies.get('size') || 'default'
  })
  app.mount('#app')
} else {
  // 作为微应用运行
  renderWithQiankun({ // 调用renderWithQiankun
    mount(props) {
      app = createApp(App)
      app
      	.use(router(props._parent_base))// 路由前缀添加router(props._parent_base),_parent_base是从主应用中传过来的
      	.use(store)
      	.use(plugins) 
      app.use(ElementPlus, {
        locale: locale,
        size: Cookies.get('size') || 'default'
      })
      app.mount(props.container ? props.container.querySelector('#app') : '#app')
    },
    bootstrap() {
      console.log('-- bootstrap --')
    },
    update() {
      console.log('-- update --')
    },
    unmount() {
      console.log('-- unmount --', app)
      app?.unmount()
    }
  })
}

当微应用不独立运行时,为什么路由前缀中要加上主应用的部署目录?

  • 首先,主应用不是部署在服务器的根目录,而是在/portal/admin-console/目录下。所以,访问该应用的任何一个页面,都需要加上该目录作为访问地址,如系统的home页,就需要这样访问:http://localhost:18080/portal/admin-console/index,index是路由,前面是服务域名+目录。
  • 其次,假设微应用的路由不加如上所讲的目录作为前缀,会怎么样呢?这是当时部署后遇到的一个最大的难题。

请添加图片描述

如上图所示,我们访问了vite-vue3-app2/hello这个路由,而在点击后却跳转到了http://localhost:8082/vite-vue3-app2/portal/admin-console/vite-vue3-app2/hello,页面404错误。对比之下就会发现,qiankun在调起微应用时,将微应用的路由前缀添加在了主应用的域名后面,我们之所以在主应用中添加与微应用前缀一致的路由名称,也是因为这个原因。

当访问以上页面时,访问的域名是http://localhost:8082,所以原本微应用中的路由前缀就会添加在8082后面,而不是添加在/portal/admin-console/后面。

而当我们按照如上所讲的,将微应用中的路由前缀改为${parentBase}${packagejson.name}时(请参考如下2.微应用路由定义),访问就会变的正常。如果主应用部署在根目录,则parenBase给空值。

这一点对于部署在非根目录的服务非常重要,但在官方API中没有说明。是在开发环境调试正常,部署之后遇到的巨坑。

  1. 微应用路由定义:router.js文件
import { createWebHistory, createRouter } from 'vue-router'
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'

export const constantRoutes = [
  {
    path: '/home',
    component: () => import('@/views/micro/home.vue'),
    hidden: true
  },
  {
    path: '/hello',
    component: () => import('@/views/micro/hello.vue'),
    hidden: true
  },
  {
    path: '/about',
    component: () => import('@/views/micro/about.vue'),
    hidden: true
  },
];

const router = (parentBase) => {
    /**
     * 区别作为微应用运行和独立运行时的路由base
     * 1. 当作为微应用运行时:路由前缀为 ${parentBase}${packagejson.name}`
     *      - parentBase是从主应用中传过来的参数
     *      - packagejson.name是在package.json文件中定义的固定变量,是为了方便使用和便于区分,这个应该大家都能够理解
     * 2. 独立运行时:路由前缀为/app2
     */
    const base = qiankunWindow.__POWERED_BY_QIANKUN__ ? `${parentBase}${packagejson.name}` : '/app2'
    return createRouter({
        history: createWebHistory(base),
        routes: constantRoutes,
        scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition
        } else {
            return { top: 0 }
        }
        },
    });
}

export { router }

  1. vite.config.js文件修改
import { defineConfig, loadEnv } from 'vite'
import path from 'path'
import createVitePlugins from './vite/plugins'

export default defineConfig(({ mode, command }) => {
  const env = loadEnv(mode, process.cwd())
  const { VITE_APP_ENV, VITE_WEB_APP_DIR } = env
  return {
    configureWebpack: { // webpack 配置
      devtool: 'source-map',
    },
    /**
     * 代理的问题:微应用打包的 base 必须跟主应用中的代理地址 entry 值一致
     * 但是,加上 /app2/ 之后,就必须部署在 app2 目录,否则无法独立访问。
     */
    base: '/app2/',
    build: {
      assetsDir: "assets",
    },
    plugins: createVitePlugins(env, command === 'build'),
    resolve: {
      alias: {
        '~': path.resolve(__dirname, './'),
        '@': path.resolve(__dirname, './src')
      },
      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
    },
    server: { ... }, // server配置省略
  }
})

vite.config.js中的配置,主要是修改base值,如代码片段中的注释:

  • 微应用打包的 base必须跟主应用中的代理地址 entry 值一致。
  • 微应用必须部署在base 目录,否则无法独立访问。
  1. package.json文件修改
    package.json 文件中主要是修改了name值,这是为了方便在router中使用,全局使用统一的变量值更便于理解。你也可以自己选择修改或不修改,这里的值改动,不会影响整体功能的运行

部署

  1. 主应用部署

主应用部署在\portal\admin-console目录下,如下图所示nginx服务目录结构:
在这里插入图片描述
nginx配置:


    server {
        listen       18080;
        server_name  localhost;

        ......其它内容省略

		location /portal/admin-console/ {
			root app;
			index index.html;
			// 主应用是history模式,解决404问题
			try_files $uri $uri/ /portal/admin-console/index.html;
		}
		
		// 后端服务的代理
		location /dev-api/ {
			proxy_pass https://xxxxxxxxxxx/;
		}
		
		// 微应用代理
		location /app2/ {
			proxy_pass http://localhost:18089/app2/; // 微应用部署的服务
		}
		
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

  1. 微应用部署
    如前所示,微应用需要部署在/app2目录下,微应用的静态文件路径:
    在这里插入图片描述
    nginx配置:

    server {
        listen       18089;
        server_name  localhost;
        
        // ......其它内容省略
        
		location /app2/ {
			root app;
			index index.html;
			// 404
			try_files $uri $uri/ /app2/index.html;
		}
		
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

至此,qiankun微应用接入成功。主应用、微应用单独访问或在主应用中访问微应用,都能够正常访问。

在改造的过程中,遇到了两个难题:

  1. entry微应用的入口。官方只给出了很多个方案,但都没有我想要的:在菜单中添加微应用,还要通过代理来激活。这个问题的解决都还相对容易,比较难搞的是下面一个问题。
  2. 主应用部署在了非根目录下,出现如上所述访问跳转错误的问题。这个问题的解决只能通过不断观察,观察每次路由访问时出现错误的情况,一步一步逐层破解,直到最后发现解决问题的办法。实际上,问题的最终解决看似很简单,但是破解问题的过程并不容易。参考官方API将每一步都测试没有问题了,但我的问题就是存在,翻遍搜索引擎也没有找到我想要的答案,只能自己一步一步破解。解决问题的过程很难熬,但解决之后的成就感也是满满的!

所以,遇到问题 还是可以多探索探索的呀!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/409038.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web前端文件上传可选择的4种方式

在web前端开发中&#xff0c;文件上传属于很常见的功能&#xff0c;不论是图片、还是文档等等资源&#xff0c;或多或少会有上传的需求。一般都是从添加文件开始&#xff0c;然后读取文件信息&#xff0c;再通过一定的方式将文件上传到服务器上&#xff0c;以供后续展示或下载使…

WEB核心【记录网站登录人数,记录用户名案例】Cookie技术实现

目录 &#x1f482; 个人主页: 爱吃豆的土豆&#x1f91f; 版权: 本文由【爱吃豆的土豆】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 &#x1f3c6;人必有所执&#xff0c;方能有所成&#xff01; &…

npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree

当我们拿到一个前端项目的时候&#xff0c;想要把它运行起来&#xff0c;首先是要给它安装依赖&#xff0c;即cd到当前项目根目录下去执行npm install命令&#xff0c;然后有一定几率在终端你会遇到这样的报错&#xff1a; npm ERR! code ERESOLVEnpm ERR! ERESOLVE unable to…

HTML基础————table标签

1.什么是table&#xff1f; table是html中的表格&#xff0c;由tr、td、th元素组成。 tr 元素定义表格行&#xff0c;th 元素定义表头&#xff0c;td 元素定义表格单元 2.可选的属性 属性描述border定义表格的边框cellpadding单元格内内容与单元格边的间距cellspacing单元格…

Vite配置环境变量

Vite配置代码环境变量和 Vue2 的配置差不多&#xff0c;都是依靠 .env 文件夹来实现 详情可到 Vite 官网的环境配置里了解&#xff1a;环境变量和模式 {#env-variables-and-modes} | Vite中文网 一、在src同级下创建 .env.production 生产模式和 .env.development 开发模式两…

基于 vue3 element-plus 的暗黑模式

前言 element-plus2.2.0 已经开始支持暗黑模式了 通过在html标签上添加一个名为 dark 的类来启用 基于 vue3 & element-plus 的项目现在可以方便的添加暗黑模式 一、基本使用 因为是通过在html标签上添加 dark 类&#xff0c;可以自行实现切换 但为了方便切换以及进一…

WEB核心【会话技术】第十五章

目录 &#x1f482; 个人主页: 爱吃豆的土豆&#x1f91f; 版权: 本文由【爱吃豆的土豆】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 &#x1f3c6;人必有所执&#xff0c;方能有所成&#xff01; &…

【JavaScript】VUE前端实现微信版录制音频wav格式转化成mp3格式和Base64

一、前言 前端有个需求是要实现一个像微信一样&#xff0c;按住录音&#xff0c;松开发送语音&#xff0c;期间踩了不少坑&#xff0c;特地记录一下&#xff0c;主要用到两个库 js-audio-recorder &#xff1a;负责录制音频&#xff0c;支持的格式只有wav、pcmLAMPjs &#x…

三更Blog项目总结(p1~p40)

文章目录项目创建——多模块开发功能业务逻辑总结热门文章列表分类列表分页文章查询文章详情友链查询登录功能退出登录视频地址&#xff1a;SpringBoot项目实战-前后端分离博客项目-Java项目_哔哩哔哩_bilibili 项目创建——多模块开发 整个系统分为前台模块&#xff08;blog&…

三步解决微信小程序cdn加速(资源大小超过200k)

刚开始开发小程序的时候&#xff0c;上传代码会遇到这样的问题&#xff1a;图片和音频资源大小超过 200 K 下面是官方建议 【建议】小程序代码包里可以存放一些必要的静态资源&#xff08;例如tabbar的icon等&#xff09;&#xff0c;不过静态资源体积过大也会影响小程序代码…

运行安装vue3+vite+Ts项目报错,无法加载vite.config.ts文件(failed to load config from D:\XXX\vite.config.ts)

git 上面拉别人的vue3viteTs项目&#xff0c; 安装依赖成功之后运行&#xff0c;出现报错 failed to load config from D:\XXX\vite.config.ts 百度搜索的结果是用 pnpm进行下载 然后卸载node_modules文件进行重新下载&#xff0c;这时候有出现问题 自己的node版本太低。如…

【Node.js+koa--后端管理系统】设计动态发布、修改、查询、删除接口

&#x1f373;作者&#xff1a;贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛\color{#FF0000}{贤蛋 大眼萌 &#xff0c;一名很普通但不想普通的程序媛}贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛&#x1f933; &#x1f64a;语录&#xff1a;多一些不为什么的…

级联选择器(el-cascader)动态加载(lazyLoad)实现省市区三级选择

开开心心工作&#xff0c;兢兢业业生活 一、实现省市区级联选择&#xff08;插件&#xff09; 1. 需求&#xff1a;实现一个省市区的级联选择器&#xff0c;点击一级&#xff0c;动态加载下一级 那好&#xff0c;我们找个轮子 2. 他山之石&#xff08;找个轮子&#xff09; E…

Eclipse配置Tomcat详细教程,其中包含如何使用eclipse+tomcat创建并运行web项目

一&#xff1a;Tomcat的下载官网:Apache Tomcat - Welcome!https://tomcat.apache.org/ 进入官网显示如图所示的界面&#xff0c;在下下载的是Tomcat9.0版本&#xff0c;你可以自己选一款。 点击然后进入下面这个界面&#xff1a; 在下电脑是64位&#xff0c;所以 在下下载的6…

【vue】仿PC端微信制作聊天框

前言 采用(vuevue-routervuexes6stylus) 来实现一个仿PC端微信的小demo&#xff0c;可以使用一个智能api&#xff0c;实现智能对话。欢迎大家对鄙人提出宝贵意见&#xff0c;相互学习讨论&#xff0c;一起进步。 demo地址 源码地址 demo做的是PC版&#xff0c;建议使用电脑预…

我的VUE 学习之路(下)

前言&#xff1a; 在经历过前面在HTML下的VUE相关基础的洗礼后&#xff0c;我们可以动手去做一些事了&#xff0c;此时发现直接通过直接VUE组件方式与之前在HTML不同&#xff0c;首先要“静一静”&#xff0c;细看之下只是对之前的很多写法做了封装。 本文旨在直接上手Vue项目下…

FormData详解

FormData 接口提供了一种表示表单数据的键值对 key/value 的构造方式&#xff0c;并且可以轻松的将数据通过XMLHttpRequest.send() 方法发送出去&#xff0c;本接口和此方法都相当简单直接。如果表单 enctype 属性设为 multipart/form-data &#xff0c;则会使用表单的 submit(…

vue项目控制台报错信息问题记录:Uncaught TypeError: Cannot read properties of null (reading ‘setAttribute‘)

在写vue项目的时候&#xff0c;控制台总是报错如下代码&#xff1a; 1、Uncaught TypeError: Cannot read properties of null (reading ‘setAttribute‘) 主要是因为某些代码书写不规范&#xff0c;导致templete解析不出来&#xff0c;从而报错 解决方案&#xff1a; 检查下…

uniapp 实现生成海报并分享给微信好友和保存到本地相册

记录uniapp 生成二维码海报并保存到本地或者分享给微信好友 – 文章目录记录uniapp 生成二维码海报并保存到本地或者分享给微信好友前言一、引入生成二维码的组件二、点击右侧的分享图标生成海报三&#xff1a;将canvas 图片转化成图片&#xff08;最关键&#xff09;四&#…

el-tree设置利用setCheckedNodes/setCheckedKeys默认勾选节点,以及通过setChecked新增勾选指定节点

实现目标&#xff1a;在生成el-tree时&#xff0c;默认勾选其中某几个选项&#xff1b;或在进行某个选项的选中时&#xff0c;同时勾选上另一个选项。 实现效果&#xff1a; 在生成树结构时&#xff0c;默认勾选其中的两个选项。 在勾选其中一个选项时&#xff0c;另一个选项…