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

相关推荐
林晓lx10 小时前
使用Git钩子+ husky + lint语法检查提高前端项目代码质量
前端·git·gitlab·源代码管理
王同学要变强10 小时前
【深入学习Vue丨第二篇】构建动态Web应用的基础
前端·vue.js·学习
程序定小飞10 小时前
基于springboot的web的音乐网站开发与设计
java·前端·数据库·vue.js·spring boot·后端·spring
Hello_WOAIAI10 小时前
2.4 python装饰器在 Web 框架和测试中的实战应用
开发语言·前端·python
FinClip11 小时前
凡泰极客亮相香港金融科技周,AI助力全球企业构建超级应用
前端
阿四11 小时前
【Nextjs】为什么server action中在try/catch内写redirect操作会跳转失败?
前端·next.js
申阳11 小时前
Day 6:04. 基于Nuxt开发博客项目-LOGO生成以及ICON图标引入
前端·后端·程序员
中国lanwp11 小时前
npm中@your-company:registry 和 registry 的区别
前端·npm·node.js
Bacon11 小时前
Electron 应用商店:开箱即用工具集成方案
前端·github
行走的陀螺仪11 小时前
uni-app + Vue3 实现折叠文本(超出省略 + 展开收起)
前端·javascript·css·uni-app·vue3