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模式,也会html的head头也会污染父应用,很奇妙,因为父应用的资源都加载失败了,具体来说就是加载了hybrid所在的盘的目录的根目录。
  7. 想到在web端部署一切正常,所以肯定是在hybrid端这个环境导致的问题。而且在子应用出来的path和fullPath都有问题,就判断是vue-router在hybrid环境导致的解析问题
  8. 我不想读源码,直接扔给ai去读vue-router的源码,定位到原因,就是normalizeBase、resolvePath、parsePath 、getLocation这四个函数导致的,其中就是读取了window.location,所以肯定会读到file协议,所以解析下来的都是错的。我需要做的就是把他做兼容。让ai给了一些改动建议
  9. 用了patch-package和postinstall-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时代排查问题就是高效~~~

相关推荐
三天不学习5 小时前
Egg.js 4 突然爆火,原因是否归结于AI 原生落地需求爆发?
前端·javascript·全栈·egg.js
xcs194056 小时前
前端 vue 的前端页面debugger 进不去
前端·javascript·vue.js
明月_清风7 小时前
Deno 终局来了:从挑战 Node 到被 Cloudflare 收编
前端·后端·node.js
Csvn8 小时前
框架性能优化
前端
回眸&啤酒鸭9 小时前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
用户693717500138410 小时前
2026,程序员的时代拐点到了
android·前端·后端
大龄秃头程序员10 小时前
一次 iBeacon + BLE 无感解锁方案的实现记录
前端
小兔子10 小时前
Python 的 GIL 与 free-threading:3.13 之后「去 GIL」走到哪一步了
前端
IT_陈寒10 小时前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端
guslegend11 小时前
脚手架原理与本地调试:从 bin 软链接到 npm link
前端·npm·node.js·脚手架·前端工程化