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时代排查问题就是高效~~~

相关推荐
Henrii_历小海20 分钟前
WhatsApp Web 不是手机镜像:多设备协议、本地数据流与出海获客流水线
前端·智能手机
ayqy贾杰27 分钟前
Claude Fable 5 提示词泄漏,抓紧学习下
前端·后端·面试
To_OC9 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic9 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy9 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志10 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble11 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯12 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜12 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司12 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能