首页 > 其他分享 >京东微前端应用MicroApp,主应用vite-vue3,子应用vite-vue3+pinia

京东微前端应用MicroApp,主应用vite-vue3,子应用vite-vue3+pinia

时间:2023-06-20 09:22:29浏览次数:64  
标签:subMenu hash route item 应用 vue3 const vite

micro-app 官方地址
micro-app 官方demo地址
这篇文章主要是为了记录,本人在使用中遇到的一些问题,供参考

  1. 资源找不到 -> 本地使用代理,显示nginx转发
  2. 子应用使用组件插槽或者pinia,路由懒加载报错问题 ->小项目几个路由不要懒加载,大项目中懒加载的时候不要使用pinia或者组件中不适用

网上的个人文章和官网的都差不多,都没有我遇到的问题,这篇文章只着重把我遇到的问题地方贴出来

资源找不到问题

用vite,图片资源放在public下面,使用绝对地址引用图片

  • 子应用在public文件夹中新建assets文件夹,在assets文件夹下建一个vite1 文件夹,静态资源放置于此
  • 主应用开发环境使用proxy,线上环境使用nginx转发
  • image.png

子应用使用组件插槽或者pinia,路由懒加载报错问题

  • 小项目几个路由加载页面不使用懒加载,
  • 大项目中懒加载的时候不要使用pinia或者组件中不适用

主应用为了美观重写菜单

image.png

HeaderNav 组件

export const menuList = [
  {
    title: 'home',
    appName: 'home',
    route: '/',
    children: []
  },
  {
    title: 'vite1',
    appName: 'appname-vite1',
    route: '/app-vite1',
    children: [
      {
        title: 'home',
        route: '/app-vite1/home',
        hashName: 'home',
        isShow: true
      },
      {
        title: 'page2',
        route: '/app-vite1/page2',
        hashName: 'page2',
        isShow: true
      }
    ]
  }
]
export const routeNameList = menuList.map((item) => item.route)
export const hashNameList = menuList.reduce((total, cur) => {
  const arr = cur.children.map((item) => item.hashName) || []
  return (total = [...total, ...arr])
}, [])

<!--
 * @Author: 
 * @Date: 2023-06-13 16:23:35
 * @LastEditors: 
 * @LastEditTime: 2023-06-17 14:18:29
 * @FilePath: \microApp_demo\vite_vue3_main\src\components\HeaderNav.vue
 * @Description: HeaderNav
-->
<template>
  <div id="HeaderNav">
    <div class="menu_list">
      <div
        v-for="(menu, index) in menuList"
        :key="index"
        class="menu_item_box"
        @mouseenter="mouseenter(menu.children, index)"
        @mouseleave="mouseleave"
      >
        <div v-show="mouseenterIndex === index" class="subMenu_list">
          <div
            v-for="(subMenu, subIdx) in formatChildrenShowMenu(menu.children)"
            :key="subIdx"
            :class="{
              subMenu_item: true,
              subMenu_item_active: activeSubMenuIndex === subIdx && activeMenuIndex === index
            }"
            @click="checkSubMenu(menu, subMenu, subIdx)"
          >
            <div>{{ subMenu.title }}</div>
          </div>
        </div>
        <div
          :class="{
            menu_item: true,
            menu_item_active: activeMenuIndex === index
          }"
          @click="checkMenu(menu, index)"
        >
          <div>{{ menu.title }}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import microApp, { getActiveApps } from '@micro-zoe/micro-app'
import { menuList, routeNameList, hashNameList } from './data'

const router = useRouter()

let activeRouter = 0 // 当前激活菜单的 index

let activeMenuIndex = ref(0)
let activeSubMenuIndex = ref(null)
let mouseenterIndex = ref(null)

const formatChildrenShowMenu = (subMenu) => {
  return subMenu.filter((item) => item.isShow)
}

const checkSubMenu = (menu, subMenu, index) => {
  activeSubMenuIndex.value = index
  const { appName } = menu
  const { route } = subMenu
  select(route, subMenu, appName)
}

const checkMenu = (item, index) => {
  activeMenuIndex.value = index
  activeSubMenuIndex.value = null
  const { appName, route } = item
  const subMenu = item.children
  select(route, subMenu, appName)
}

const mouseenter = (subMenu, index) => {
  let show = subMenu.filter((item) => item.isShow).length > 0
  if (show) {
    mouseenterIndex.value = index
  }
}

const mouseleave = () => {
  mouseenterIndex.value = null
}

// 根据url地址获取选中菜单
const getActiveIndex = () => {
  // location.pathname的值通常为:/main/vite1/page2,我们只取`/vite1/page2`
  const pathArr = location.pathname.match(/\/app-.+/)
  activeRouter = pathArr ? pathArr[0].replace(/\/$/, '') : '/'
  activeMenuIndex.value = menuList.findIndex((item) => item.route === activeRouter)

  let hash = ''
  if (location.hash) {
    hash = location.hash.split('?')[0]
  }
  console.log('pathArr', location.pathname, hash, activeRouter)
  // 兼容 vite 子应用,因为它们是hash路由
  if (routeNameList.includes(activeRouter) && hashNameList.includes(hash.split('/')[1])) {
    activeRouter += hash.replace(/^#/, '')
    let subMenu = menuList[activeMenuIndex.value].children
    activeSubMenuIndex.value = subMenu.findIndex((item) => item.route === activeRouter)
  }

  // 去除斜线后缀,如:/app-x/ 转换为 /app-x
  if (activeRouter !== '/') {
    activeRouter = activeRouter.replace(/\/$/, '')
  }

  return activeRouter
}

// 用户点击菜单时控制基座应用跳转
const select = (index, subMenu, appName) => {
  // 因为 vite 子应用是hash路由,所以需要传递hash值
  console.log('index', index, appName)
  let hash = null
  let path = index
  if (path.split('/').length > 2) {
    const pathArr = index.split('/')
    path = '/' + pathArr[1]
    hash = '/' + pathArr[2]
  } else {
    let show = subMenu.filter((item) => item.isShow).length > 0
    if (show) {
      activeSubMenuIndex.value = 0
    }
  }
  // 获取子应用appName
  // 控制基座跳转页面,并渲染子应用
  /**
   * 当子应用还未渲染,通过基座控制路由跳转,子应用在初始化时会自己根据url渲染对应的页面
   * 当子应用已经渲染,则直接控制子应用进行内部跳转
   *
   * getActiveApps: 用于获取正在运行的子应用
   */
  if (!getActiveApps().includes(appName)) {
    // child-vite 子应用为hash路由,这里拼接一下hash值
    hash && (path += `/#${hash}`)
    // 主应用跳转
    router.push(path)
  } else {
    let childPath = null
    // child-vite 子应用是hash路由,hash值就是它的页面地址,这里单独处理
    if (hash) {
      childPath = hash
    } else {
      // path的值形式如:/app-xxx/page2,这里/app-xxx/page2才是页面地址,所以我们需要将/app-xxx部分删除
      childPath = path.replace(/^\/app-[^/]+/, '')
      !childPath && (childPath = '/') // 防止地址为空
    }
    // 主应用通过下发data数据控制子应用跳转
    microApp.setData(appName, { path: childPath })
  }
}

onMounted(() => {
  getActiveIndex()
  microApp.addGlobalDataListener((data) => {
    // console.log("全局数据:", data);
    getActiveIndex()
  })
  window.addEventListener('popstate', () => getActiveIndex())
})
</script>

<style lang="scss" scoped>
#HeaderNav {
  width: 100%;
  height: 60px;
  display: flex;
  align-items: center;
}
.menu_item,
.subMenu_item {
  width: 166px;
  height: 60px;
  font-size: 20px;
  font-family: MiSans-Demibold, MiSans;
  font-weight: 600;
  background: rgba(29, 29, 31, 0.05);
  border-radius: 4px 4px 4px 4px;
  color: rgba(29, 29, 31, 0.66);
  letter-spacing: 1px;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: all 0.2s;
  backdrop-filter: blur(9px);
  cursor: pointer;
}
.menu_list {
  position: relative;
  display: flex;
  align-items: center;
  .menu_item_box {
    position: relative;
    display: flex;
    align-items: flex-end;
  }

  .menu_item {
    position: relative;
    margin: 0 10px;
  }
  .menu_item_active,
  .subMenu_item_active {
    background: #00a8ff;
    color: #fafafb;
  }
  .subMenu_list {
    position: absolute;
    top: 60px;
    left: 50%;
    transform: translate(-50%);
    width: auto;
    height: 60px;
    display: flex;
    .subMenu_item {
      width: 166px;
      height: 60px;
      margin: 0 10px;
    }
  }
}
</style>

路由改变菜单高亮改变

  • 子应用中全局监听路由改变事件
const route = useRoute();

const onRouteChange = () => {
  if (window.eventCenterForAppNameVite) {
    // 发送全局数据,通知侧边栏修改菜单展示
    window.eventCenterForAppNameVite.setGlobalData({ name: "child-vite1" });
  }
};

watch(
  () => route.name,
  () => {
    onRouteChange();
  }
);

demo地址

项目安装启动

  • 分别进入 vite_vue3_main 和 vite_vue3_vite1 两个文件夹
  • 执行 yarn 或者 npm install 安装依赖
  • 执行 yarn dev 或者 npm run dev 运行项目
  • 执行 yarn build 或者 npm run build 打包

打包部署

  • 主应用生成的打包文件是main文件夹,放到 nginx 的html文件夹中
  • 子应用生成的打包文件是vite1文件夹,放到 nginx 的child文件夹中

nginx启动

  • 双击 nginx.exe
  • 打开项目127.0.0.1/main/
  • 配置文件是 conf 文件夹中的 nginx.conf 文件
  • 修改完配置文件,执行 ./nginx -s reload 重启nginx

作者:白马不是马

标签:subMenu,hash,route,item,应用,vue3,const,vite
From: https://www.cnblogs.com/DTCLOUD/p/17492751.html

相关文章

  • 自然语言处理 Paddle NLP - 信息抽取技术及应用
    1.什么是信息抽取即自动从无结构或半结构的文本中抽取出结构化信息的任务(病历抽取)2.实体抽取3.关系抽取4.事件抽取信息抽取和知识图谱是一个上下游的关系。抽取的结果,可以组装成知识图谱(一种存储知识的结构)医疗、金融、法律,三大行业用得比较多从问诊中抽取信息贷款......
  • 动手开发第一个 SAP Fiori Elements 应用
    本教程的前五篇文章,我们已经为SAPFioriElements的创建做了足够的铺垫。0.迈入SAPFioriElements开发的大门-什么是FioriElements,它和FreestyleUI5开发方式有何区别?1.SAPFioriElements开发环境的搭建和开发准备工作2.在ES5系统注册用户以获得Fiori......
  • Linux环境下I2C应用程序编写
    原文:https://blog.csdn.net/propor/article/details/129667596本文介绍Linux环境下,对I2C设备进行操作。在对I2C总线进行操作时,可采用i2c-tools对I2C进行查看及操作,待通过工具可对I2C进行操作后,再编写程序进行操作。1.i2c-tools的使用1)安装sudoaptinstalli2c-tools2)查询......
  • Html使用Vue3+ElementPlus(图标 ElMessage)
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><metahttp-equiv="X-UA-Compatible"content="IE=edge"/><metaname="viewport"content="wid......
  • Linux 操作系统及应用考试题
    Linux操作系统及应用考试题要求:操作题截屏到WORD文档,文件名字为学号+姓名。每道题如能显示,应显示你的用户名,必要的步骤需截图,图片不要太大。考试操作题(100分)。......
  • To ChatGPT:让你更加随意地使用所有ChatGPT应用
    现在其实已经有很多在线的llm服务了,当然也存在许多开源部署方案,但是不知道大家有没有发现一个问题,目前基于ChatGPT开发的应用,都是使用的OpenAI的接口。换句话说,如果没有OpenAI账号,就没有办法使用这些应用。但是其实这些应用并不是强依赖于OpenAI的接口,其他的在线llm服务也是可以的......
  • mix-blend-mode和background-blend-mode应用场景
    mix-blend-mode使多重叠元素的颜色发生混合,包括元素与元素,元素与图片background-blend-mode使得多个背景发生混合,包括背景图与背景图,背景图与背景色isolation:isolate可以创建层叠上下文,就可以阻断mix-blend-mode,使多个元素能分组进行混合,不干扰常用场景1.图片后方的元素......
  • 记录--Vue3 封装 ECharts 通用组件
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助按需导入的配置文件配置文件这里就不再赘述,内容都是一样的,主打一个随用随取,按需导入。import*asechartsfrom"echarts/core";//引入用到的图表import{LineChart,typeLineSeriesOption}from"echarts/......
  • Angular 应用里 NullInjectorError - No provider for XX 错误的一个场景和分析过程
    最近处理客户incident,有个客户从Spartacus4升级到5.2之后,启动Storefront,console遇到了一个错误消息:NullInjectorError-NoproviderforAnonymousConsentTemplatesAdapter!引起这个错误消息的场景有很多,这个incident最后的场景是:以前的module通过loadedfor......
  • 【工程应用八】终极的基于形状匹配方案解决(小模型+预生成模型+无效边缘去除+多尺度+各
      我估摸着这个应该是关于形状匹配或者模版匹配的最后一篇文章了,其实大概是2个多月前这些东西都已经弄完了,只是一直静不下来心整理文章,提醒一点,这篇文章后续可能会有多次修改(但不会重新发文章,而是在后台直接修改或者增加),所以有需要的朋友可以随时重复查看。 这次带来的更新......