micro-zoe子应用路由路径污染问题

背景

公司需要做一个hybrid app,而且这个可以兼容web端,所以做在同一个项目。项目需要内嵌其他业务部分的项目,很自然地想到用micro-zoe/microapp来做微前端方案。

技术栈

windows系统 父应用: vue3全家桶+micro-zoe/microapp,路由是用hash模式 子应用:vue2全家桶,yarn,路由是用histroy模式

遇到问题.

hybrid端,父应用正常加载,子应用只加载了main.js,没有加载页面。

排查定位&尝试过的方案

  1. 在web端,父应用和子应用都正常加载。起码说明micro-app的配置是没有问题的,子应用的配置是iframe模式,路由模式是pure
  2. 在hybrid端,子应用打印出来的this.$route,路由信息带了带了windows的盘符,譬如说,页面路由是/pageA,但是打印出来的是/D:/pageA
  3. 基于2的信息,判断是路由history模式导致的,具体来说,应该是路由配置的base参数有关
  4. 尝试过在父应用传参子应用<micro-app baseroue="/"></micro-app>,也试过<micro-app baseroue="http://domain.com/"></micro-app>也不行,在子应用console出来的还是空字符串,原来配置了pure模式的虚拟路由,就会透传baseroute的,所以在子应用通过__MICRO_APP_BASE_ROUTE__读取出来时空字符串
  5. 也尝试过在子应用的new Router的时候写死配置base为/,也不行
  6. 搜了ai,尝试了<base href="/">的方案,也不行,而且发现了就算是iframe模式,也会htmlhead头也会污染父应用,很奇妙,因为父应用的资源都加载失败了,具体来说就是加载了hybrid所在的盘的目录的根目录。
  7. 想到在web端部署一切正常,所以肯定是在hybrid端这个环境导致的问题。而且在子应用出来的pathfullPath都有问题,就判断是vue-routerhybrid环境导致的解析问题
  8. 我不想读源码,直接扔给ai去读vue-router的源码,定位到原因,就是normalizeBaseresolvePathparsePath getLocation这四个函数导致的,其中就是读取了window.location,所以肯定会读到file协议,所以解析下来的都是错的。我需要做的就是把他做兼容。让ai给了一些改动建议
  9. 用了patch-packagepostinstall-postinstall来打补丁,具体的方法不赘述了

最后的解决方案

  1. 需要锁定当前的vue-router版本,以防版本变动
  2. 修改vue-router/dist/vue-router.esm.js
js 复制代码
export function getLocation(base: string): string {
  let path = window.location.pathname

  // 直接在原 path 变量上处理,不新增变量
  if (window.location.protocol === 'file:') {
    path = path
      .replace(/^\/[a-zA-Z]:\//, '/')
      .replace(/^\/(Volumes|Users|Applications)\/[^/]+(\/|$)/, '/');
  }

  const pathLowerCase = path.toLowerCase()
  const baseLowerCase = base.toLowerCase()

  if (base && ((pathLowerCase === baseLowerCase) ||
    (pathLowerCase.indexOf(cleanPath(baseLowerCase + '/')) === 0))) {
    path = path.slice(base.length)
  }

  return (path || '/') + window.location.search + window.location.hash
}

export function parsePath(path: string): {
  path: string;
  query: string;
  hash: string;
} {
  let hash = ''
  let query = ''

  const hashIndex = path.indexOf('#')
  if (hashIndex >= 0) {
    hash = path.slice(hashIndex)
    path = path.slice(0, hashIndex)
  }

  const queryIndex = path.indexOf('?')
  if (queryIndex >= 0) {
    query = path.slice(queryIndex + 1)
    path = path.slice(0, queryIndex)
  }

  // 直接修改原 path 变量,不新增 cleanPath
  path = path
    .replace(/^\/[a-zA-Z]:\//, '/')
    .replace(/^\/(Volumes|Users|Applications)\/[^/]+(\/|$)/, '/');

  return {
    path,
    query,
    hash
  }
}

export function resolvePath(
  relative: string,
  base: string,
  append?: boolean
): string {
  // 直接在原变量上处理,不新增 cleanBase/cleanRelative
  if (base) {
    base = base
      .replace(/^\/[a-zA-Z]:\//, '/')
      .replace(/^\/(Volumes|Users|Applications)\/[^/]+(\/|$)/, '/');
  }
  if (relative.charAt(0) === '/') {
    relative = relative
      .replace(/^\/[a-zA-Z]:\//, '/')
      .replace(/^\/(Volumes|Users|Applications)\/[^/]+(\/|$)/, '/');
  }

  const firstChar = relative.charAt(0)
  if (firstChar === '/') {
    return relative
  }

  if (firstChar === '?' || firstChar === '#') {
    return base + relative
  }

  const stack = base.split('/')

  if (!append || !stack[stack.length - 1]) {
    stack.pop()
  }

  const segments = relative.replace(/^\//, '').split('/')
  for (let i = 0; i < segments.length; i++) {
    const segment = segments[i]
    if (segment === '..') {
      stack.pop()
    } else if (segment !== '.') {
      stack.push(segment)
    }
  }

  if (stack[0] !== '') {
    stack.unshift('')
  }

  return stack.join('/')
}

function normalizeBase(base: ?string): string {
  if (!base) {
    if (inBrowser) {
      const baseEl = document.querySelector('base')
      base = (baseEl && baseEl.getAttribute('href')) || '/'
      // 复用 base 变量,链式处理协议和系统前缀
      base = base
        .replace(/^[a-zA-Z]+:\/\/[^\/]+/, '') // 移除所有协议
        .replace(/^\/[a-zA-Z]:\//, '/') // 移除 Windows 盘符
        .replace(/^\/(Volumes|Users|Applications)\/[^/]+(\/|$)/, '/'); // 移除 macOS 根目录
    } else {
      base = '/'
    }
  }
  if (base.charAt(0) !== '/') {
    base = '/' + base
  }
  return base.replace(/\/$/, '')
}
  1. 注意,需要跟你的项目打包target决定改哪个vue-router/dist/vue-router.*.js

总结

ai时代排查问题就是高效~~~

相关推荐
勇敢di牛牛3 分钟前
Vue+mockjs+Axios 案例实践
前端·javascript·vue.js
詩句☾⋆᭄南笙13 分钟前
HTML列表、表格和表单
服务器·前端·html·表格·列表·表单
IT_陈寒25 分钟前
Python性能翻倍的5个冷门技巧:从GIL逃逸到内存视图的实战优化指南
前端·人工智能·后端
南城巷陌30 分钟前
错误边界:用componentDidCatch筑起React崩溃防火墙
前端·react.js·前端框架
FinClip36 分钟前
OpenAI推出Apps SDK,你的企业App跟上了吗?
前端·app·openai
馨谙41 分钟前
Linux中的管道与重定向:深入理解两者的本质区别
前端·chrome
夏天想1 小时前
复制了一个vue的项目然后再这个基础上修改。可是通过npm run dev运行之前的老项目,发现运行的竟然是拷贝后的项目。为什么会这样?
前端·vue.js·npm
@大迁世界1 小时前
这个 CSS 特性,可能终结样式冲突
前端·css
zzzsde1 小时前
【C++】深入理解string类(5)
java·前端·算法
袁煦丞1 小时前
随机菜谱解救选择困难!YunYouJun/cook 成为你的厨房锦囊:cpolar内网穿透实验室第549个成功挑战
前端·程序员·远程工作