vue2+微前端qiankun从搭建到部署的实践(主子应用切换;集成vue3+vite3子应用)

news2024/11/27 20:29:34

一、最终效果

在这里插入图片描述

二、微前端(qiankun)介绍及为什么选择用微前端,可以看官网

三、目录结构如下

在这里插入图片描述

四、具体配置

一、主应用配置

1、主应用技术栈

Vue-cli4搭建项目+Vue2+Element-Ui+qiankun;Vue2+Element-Ui+qiankun

2、搭建好主项目(即:常规的vue2后台管理系统),后续我会开源一套vue2后台管理系统模板

3、在主项目目录src下新建一个micro-app.js文件(main/src/micro-app.js)

整合所有子应用,为了方便管理(后续可以在系统管理中开个微前端配置页面——调用接口来获取)


const microApps = [
  {
    name: 'portal-fe',
    // entry: process.env.VUE_APP_SUB_VUE2,
    entry: '//localhost:9100/wocwin-qiankun/app-vue2/',
    activeRule: '/wocwin-qiankun/app-vue2'
  },
  {
    name: 'vue3-vite-fe',
    // entry: process.env.VUE_APP_SUB_VUE3,
    entry: '//localhost:3300/app-vue3vite/',
    activeRule: '/app-vue3vite'
  }
]
const apps = microApps.map(item => {
  return {
    ...item,
    container: '#app', // 子应用挂载的div
    props: {
      routerBase: item.activeRule // 下发基础路由
    }
  }
})
export default apps

4、在main.js中注册子应用(main/src/main.js)

import { registerMicroApps, start } from 'qiankun'
// 获取所有子应用
import microApps from './micro-app'

// 给子应用配置加上loader方法
const apps = microApps.map(item => {
  // console.log('app', item)
  return {
    ...item
  }
})
registerMicroApps(apps, {
  beforeLoad: (app) => {
    console.log('before load', app)
    switch (app.name) {
      case 'portal-fe':
        document.title = 'vue2常规模板'
        break
      case 'vue3-vite-fe':
        document.title = 'vue3+vite模板'
        break
    }
  },
  beforeMount: [(app) => {
    console.log('before mount', app.name)
  }]
})
start({
  prefetch: false // 取消预加载
})

5、具体跳转子应用需要用到window.history.pushState()

注意:调用这个方法前提:路由是 history 模式

window.history.pushState(state, title, url)
// 1、state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地,重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null。
///2、title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。
// 3、url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

二、vue2子应用配置

1、vue2子应用技术栈跟主项目一样

2、具体需要修改以下几个部分(跟主应用的micro-app.js一致)

1、子应用的package.json ,name要与microApps 的name一样

2、如下修改src/main.js文件

let instance = null
export function render(props = {}) {
  console.log('执行子应用渲染')
  if (window.__POWERED_BY_QIANKUN__) {
    // eslint-disable-next-line no-undef
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
  }
  const { container } = props
  instance = new Vue({
    router,
    store,
    render: (h) => h(App)
  }).$mount(container ? container.querySelector('#app') : '#app')
  console.log('开始加载相关内容')
}
// TODO:非qiankun环境下单独运行
// @ts-ignore
if (!window.__POWERED_BY_QIANKUN__) {
  console.log('并不是qiankun渲染')
  render()
}
/**
 * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap() {
  console.log('react app bootstraped')
}

/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props) {
  console.log('应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法', props)
  props.onGlobalStateChange((state) => {
    console.log('子应用接收的参数', state)
    if (state.menuId) {
      store.commit('SET_MENU_ID', state.menuId)
    }
  }, true)
  render(props)
}
/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props) {
  if (instance) {
    instance.$destroy()
    instance = null
  }
  await store.dispatch('tagsView/delAllViews')
  console.log('应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例', props)
}
/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update(props) {
  console.log('update props', props)
}

3、vue.config.js文件新增以下代码

const packageName = require('./package.json').name
module.exports = {
 publicPath: '/wocwin-qiankun/app-vue2', // 这个要与主应用的micro-app.js中activeRule一致
// 解决本地启动主子切换报跨域问题
devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  },
  configureWebpack: {
   // 把子应用打包成 umd 库格式
    output: {
      library: `${packageName}`,
      libraryTarget: 'umd', // 把微应用打包成 umd 库格式
      jsonpFunction: `webpackJsonp_${packageName}`,
      filename: `[name].[hash].js`,
      chunkFilename: `[name].[hash].js`
    }
  }
}

4、如下修改src/router/index.js文件

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/* Layout */
import Layout from '@/layout'
import login from '@/views/login.vue'
import redirect from '@/views/redirect.vue'

// 公共路由
export const constantRoutes = window.__POWERED_BY_QIANKUN__
  ? [
    {
      path: '/login',
      name: 'login',
      component: login,
      hidden: true,
      meta: {
        rootPage: true,
        noCache: true
      }
    },
    {
      path: '/redirect',
      name: 'redirect',
      component: Layout,
      hidden: true,
      children: [
        {
          path: ':path(.*)',
          name: 'redirectPage',
          component: redirect,
          meta: {
            noCache: true
          }
        }
      ]
    },
    {
      path: '/',
      component: Layout,
      redirect: '/base',
      // hidden: true,
      children: [
        {
          path: 'base',
          component: () => import('../views/demo/TTable/base.vue'),
          name: 'base',
          meta: { title: 'vue2常规模板', icon: 'monitor' }
        }
      ]
    }
  ]
  : [
    {
      path: '/redirect',
      component: Layout,
      hidden: true,
      children: [{
        path: '/redirect/:path(.*)',
        component: () =>
          import('@/views/redirect')
      }]
    },
    {
      path: '/login',
      component: () =>
        import('@/views/login'),
      hidden: true
    },
    {
      path: '/404',
      component: () =>
        import('@/views/error/404'),
      hidden: true
    },
    {
      path: '/401',
      component: () =>
        import('@/views/error/401'),
      hidden: true
    },
    {
      path: '/',
      component: Layout,
      redirect: '/base',
      children: [
        {
          path: 'base',
          component: () => import('../views/demo/TTable/base.vue'),
          name: 'base',
          meta: { title: 'vue2常规模板', icon: 'monitor' }
        }
      ]
    }
  ]
const router = new Router({
  base: '/wocwin-qiankun/app-vue2/',
  routes: constantRoutes,
  mode: 'history'
})
export default router

5、如下修改部分登出文件(判断是回主应用登录还是子应用登录页面)

在这里插入图片描述

二、vue3+vite3子应用配置

1、创建Vue3+Vite项目

可以参考我之前发布的vite快速搭建vue3项目文章来创建;也可以直接使用我开源Vue3.2+Ts+Vite3+Pinia+Element-Plus模板wocwin-admin

以下我以 wocwin-admin 项目为例

具体步骤可以参考这篇文章

2、抽离贴出重点代码

1、vue3Vite/src/main.ts文件修改

/**
 * 配置qiankun
 */
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
let instance: any = null
function render(props: any = {}) {
  const { container } = props
  instance = createApp(App)
  instance.use(router)
  instance.use(pinia)
  // 注册全局api方法
  instance.config.globalProperties.$api = api
  // 注册所有图标
  for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    instance.component(key, component)
  }
  // 注册ElementPlus
  instance.use(ElementPlus, {
    locale // 语言设置
    // size: Cookies.get('size') || 'medium' // 尺寸设置
  })
  instance.use(TuiPlus)
  // 全局组件祖册
  instance.component(
    'SvgIcon',
    // 如果这个组件选项是通过 `export default` 导出的,那么就会优先使用 `.default`,否则回退到使用模块的根
    SvgIcon.default || SvgIcon
  )
  instance?.mount(container ? container.querySelector('#app') : '#app')
  console.log('开始加载相关内容')
}
renderWithQiankun({
  mount(props: any) {
    render(props)
  },
  bootstrap() {
    console.log('%c', 'color:green;', ' ChildOne bootstrap')
  },
  update() {
    console.log('%c', 'color:green;', ' ChildOne update')
  },
  unmount(props: any) {
    console.log('unmount', props)
    instance.unmount()
    instance._container.innerHTML = ''
    instance = null
  }
})

if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  console.log('并不是qiankun渲染')
  render()
}

2、vue3Vite/package.json文件修改

name要与主应用的microApps 的name一样

3、vue3Vite/vite.config.ts文件修改

// 配置qiankun
import qiankun from 'vite-plugin-qiankun'
const packName = require('./package').name
export default defineConfig({
	base: '/app-vue3vite/', // 这个要与主应用的micro-app.js中activeRule一致 
	plugins: [
     ...
    // 配置qiankun
    qiankun(`${packName}`, {
      useDevMode: true
    })
  ],
 server: {
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  },
})

4、vue3Vite/src/router/index.ts文件修改

import { createWebHistory } from 'vue-router'
const router = createRouter({
  history: createWebHistory('/app-vue3vite/'), // 这个要与主应用的micro-app.js中activeRule一致 
  routes: constantRoutes,
})
export default router

三、若想实现主子应用之间通信可以参考这一篇博客

四、本地部署到服务器需要配置NG(可以参考下面代码)

这种配置的好处:所有子应用都是挂载在主应用中,子应用无需在新开一个端口,若服务器部署成功,单独子应用项目地址:主应用地址+子应用的publicPath

################################################
#### 门户 PC前端主-子项目NGINX统一代理规则 ####
################################################

# nginx配置
server {
    listen       8100;
    server_name  localhost;
    gzip on;
    gzip_buffers 32 4K;
    gzip_comp_level 6;
    gzip_min_length 100;
    gzip_types application/javascript text/css text/xml;
    gzip_disable "MISE [1-6]\.";
    gzip_vary on;

    # pc端主应用
    location / {
        add_header Access-Control-Allow-Origin *;
        add_header Cache-Control no-cache;
        # root 根目录,默认nginx镜像的html文件夹,可以指定其他
        root   /data/build/nodejs/wocwin-qiankun-main/dist-wocwin-qiankun-main;
        index  index.html index.htm;
        # 如果vue-router使用的是history模式,需要设置这个
        try_files $uri $uri/ /index.html;
		if ($request_filename ~* ^.*?\.(doc|pdf|docx)$) {
            add_header Content-Disposition "attachment";
			add_header Content-Type application/octet-stream;
		}
    }

    #### vue2常规模板
    location /app-vue2 {
        add_header Access-Control-Allow-Origin *;
        add_header Cache-Control no-cache;
        alias /data/build/nodejs/portal-fe-dev/dist-portal-fe/;
        index  index.html index.htm;
        try_files $uri $uri/ /app-vue2/index.html;
		if ($request_filename ~* ^.*?\.(doc|pdf|docx)$) {
            add_header Content-Disposition "attachment";
			add_header Content-Type application/octet-stream;
		}
    }

    ## vue3模板
    location /app-vue3vite {
        add_header Access-Control-Allow-Origin *;
        add_header Cache-Control no-cache;
        # root 根目录,默认nginx镜像的html文件夹,可以指定其他
        alias /data/build/nodejs/vue3-vite-fe-dev/dist-vue3-vite-fe/;
        index  index.html /index.htm;
        # 如果vue-router使用的是history模式,需要设置这个
        try_files $uri $uri/ /app-vue3vite/index.html;
        if ($request_filename ~* ^.*?\.(doc|pdf|docx)$) {
            add_header Content-Disposition "attachment";
            add_header Content-Type application/octet-stream;
        }
    }
    #######################

    # 转发后端接口
    location ^~ /sso/ {
        proxy_pass http://*********/sso/;     # 统一登录
    }
    location ^~ /user/ {
        proxy_pass http://*********/user/;   # 统一用户
    }
    # 单个子应用业务后台接口地址
		location ^~ /mes/ {
				proxy_pass http://******/mes/;
			}

    ###############################
    ### WS转发配置
    ###############################
    # 单个子应用websocket地址
    location ^~ /***/ws/ {
        proxy_pass http://****/ws/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header X-Real-IP $remote_addr;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/nginx/html;
    }
}

五、源码地址

gitHub组件地址

gitee码云组件地址

相关文章

基于ElementUi&Antd再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档


vite+vue3+ts项目搭建之集成qiankun

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

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

相关文章

Java集合:Map的使用

1.Map框架l----Map:双列数据,存储key-value对的数据 ---类似于高中的函数: y f(x)|----HashMap:作为Map的主要实现类, 线程不安全的,效率高;可以存储null的key和value|----LinkedHashMap:保证在遍历map元素时,可以按照…

【Android】系统源码下载及编译

源码及编译 步骤 1:创建一个空目录来存放源码: mkdir aosp cd aosp步骤 2:获取最新版本的 repo 并签出 android-8.1.0_r1 分支: repo init -u https://android.googlesource.com/platform/manifest -b android-8.1.0_r1其中&am…

数据结构-考研难点代码突破(C++实现树型查找-二叉搜索树(二叉排序树))

文章目录1.二叉搜索树基本操作二叉搜索树的效率分析2. C实现1.二叉搜索树基本操作 二叉排序树是具有下列特性的二叉树: 若左子树非空,则左子树上所有结点的值均小于根结点的值。若右子树非空,则右子树上所有结点的值均大于根结点的值。左、…

蓝牙耳机哪个品牌性价比高?性价比高的无线蓝牙耳机

现如今耳机已经十分普及,大多数人会随身佩戴蓝牙耳机,相较于传统耳机,无线耳机不仅携带方便,舒适度上也更加出色。不过市面上的无线耳机种类繁多,很多朋友不知道该如何挑选,所以小编特意整理了一期性价比高…

从零开始搭建kubernetes集群环境(虚拟机/kubeadm方式)

文章目录1 Kubernetes简介(k8s)2 安装实战2.1 主机安装并初始化2.2 安装docker2.3 安装Kubernetes组件2.4 准备集群镜像2.5 集群初始化2.6 安装flannel网络插件3 部署nginx 测试3.1 创建一个nginx服务3.2 暴漏端口3.3 查看服务3.4 测试服务1 Kubernetes简…

2023如何选购适合游戏设计的电脑硬件

游戏设计涉及许多不同的学科,因此涉及许多不同的软件包。有游戏引擎本身,例如 Unreal Engine 和 Unity,以及 3D 设计软件,例如 3ds Max、Blender 和 ZBrush——等等!大多数软件开发人员都维护着这些不同应用程序的系统…

C++设计模式(14)——享元模式

亦称: 缓存、Cache、Flyweight 意图 享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。 问题 假如你希望在长时间工作后放…

U-Mail邮件系统安全攻略之邮件监控

随着互联网的普及,电子邮件已经成为现代人生活和工作中最常用的通信工具之一,在企业的内外文件往来和日常沟通中发挥着重要作用。电子邮件在获得广泛应用的同时,也让企业的重要信息安全受到很大的威胁,比如员工未经允许将公司重要…

复杂美公链技术重要特色:平行公链架构

复杂美公链技术Chain33从11月开源至今,获得众多合作方的认可,其中首创的平行公链架构被百度、阿里、360等机构认可并跟进研究,这也说明了平行公链或许是区块链普及应用的重要解决方案之一。 平行公链(以下简称平行链)是…

【ADRC控制】使用自抗扰控制器调节起动机入口压力值

以前只知道工业控制中用的是PID控制,然而最近了解到实际生产中还在使用ADRC控制,而且使用效果还优于PID控制,遂找了几篇文献学习学习。 0 引言 自抗扰控制(Active Disturbances Rejection Controller,ADRC)…

【Datawhale图机器学习】第一章图机器学习导论

图机器学习导论 学习路径与必读论文清单 斯坦福CS224W(子豪兄中文精讲)知识图谱实战DeepwalkNode2vecPageRankGNNGCNGragh-SAGEGINGATTrans-ETrans-R 图无处不在 图是描述关联数据的通用语言 举例 计算机网络新冠肺炎流行病学调查传播链食物链地铁图…

推荐一个.Ner Core开发的配置中心开源项目

更多开源项目请查看:一个专注推荐.Net开源项目的榜单 当你把单体应用改造为微服务架构,相应的配置文件,也会被分割,被分散到各个节点。这个时候就会产生一个问题,配置信息是分散的、冗余的,变成不好维护管理…

ZigBee案例笔记 - USART

文章目录1.串行通信接口简述2.串行通信接口寄存器U0CSR (0x86) -USART 0 控制和状态U0UCR (0xC4)–USART 0 UART 控制U0GCR (0xC5)–USART 0 通用控制U0BUF (0xC1) – USART 0 接收/传送数据缓存U0BAUD (0xC2) – USART 0 波特率控制3.设置串行通信接口比特率控制寄存器4.外设I…

分布式一致性算法Raft原理图释

什么是分布式一致性算法Raft 分布式一致性算法Raft:指在分布式场景下实现集群数据同步的解决方案 掌握了这个算法,就可以较容易地处理绝大部分场景的容错和数据一致性需求 Raft三大角色 跟随者(Follower):普通群众…

opencv绘制矩形和圆

大家好,我是csdn的博主:lqj_本人 这是我的个人博客主页: lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…

Go语言之 下载安装go以及vscode配置go环境

上篇请移步到Go语言之 下载安装及第一个代码_水w的博客-CSDN博客 目录 一、下载安装以及配置go环境 1 下载安装go 2 配置go环境 二、安装配置git 一、在vscode上开发golang 1 配置 2 编写代码 解决报错:go: go.mod file not found in current directory or …

J-Link RTT Viewer使用教程(附代码)

目录 RTT(Real Time Transfer)简介 使用教程 常用API介绍 RTT缓冲大小修改 使用printf重定向 官方例程 RTT(Real Time Transfer)简介 平常调试代码中使用串口打印log,往往需要接出串口引脚,比较麻烦,并且串口打印速度较慢,串…

RTT 线程间同步互斥

1.概念 在多线程实时系统中,一项工作的完成往往可以通过多个线程协调的方式共同来完成,那么多个线程之间如何 “默契” 协作才能使这项工作无差错执行?下面举个例子说明。 例如一项工作中的两个线程:一个线程从传感器中接收数据…

分享112个HTML电子商务模板,总有一款适合您

分享112个HTML电子商务模板,总有一款适合您 112个HTML电子商务模板下载链接:https://pan.baidu.com/s/13wf9C9NtaJz67ZqwQyo74w?pwdzt4a 提取码:zt4a Python采集代码下载链接:采集代码.zip - 蓝奏云 有机蔬菜水果食品商城网…

ubuntu 安装支持GPU的Docker详细步骤

安装依赖项 sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common 添加 Docker GPG 密钥 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo apt-key fingerpr…