Vue Router 的踩坑日记:从诡异重定向看base配置策略

407 阅读2分钟

问题描述

最近在处理一个 Vue 项目的登录重定向问题时,遇到了个 bug 久久未能解决,原本以为是重定向函数的处理有问题,但通过几轮 debug 后,发现背后另有玄机...

Vue 的路由模式有 HTML5HashMemory(在 Vue2 中为 abstract) 三种模式,公司的登录项目原本是使用 Memory 模式,有个 API_PREFIX_URL 环境变量,专门为客户提供定制化的请求前缀,该变量最终会其赋值给 domainCustomPrefix 变量后再把它写入到 meta 里(至于为什么是这个传递链路,不要在意这些细节(ˉ▽ˉ;)...),最终通过自定义的工具函数拿到自定义 API 前缀:getMetaDataByName('domainCustomPrefix');

然后有意思的来了,某位客户私有化部署后收到异常反馈:访问domain.com/miniapp/login时,地址栏竟自动跳转为domain.com/login/miniapp/login,导致页面404。该客户环境配置了API_PREFIX_URL='/miniapp',而通常情况下该值为空。

技术上下文

// main.ts
// 改造前(内存路由)
createRouter({
  history: createMemoryHistory(),
  routes: [...] 
});

// 改造后(HTML5模式)
createRouter({
  history: createWebHistory(import.meta.env.DEV ? '/' : '/login'),
  routes: [...]
});

问题定位

为什么当 domainCustomPrefix 存在值时(如/miniapp),会在 /miniapp 前多了个 login ,这使我百思不得其解,查阅 Vue Router 文档后,发现它专门提供了处理子目录部署的机制:

If you deploy to a subfolder, you should use the publicPath option of the CLI and the related base property of the router.

其运作原理类似于:

实际路由路径 = location.pathname.replace(base, '')

由此我们便发现了一个关键的线索:原配置硬编码了/login路径,Vue Router 的 base 参数未动态适配环境变量。

所以,我们需要基于该线索,尝试以以下思路解决问题:

  1. 统一路由基础路径(base)
  2. 建立domainCustomPrefix与路由base的动态映射

解决方案

  1. 获取基础路径
const routerBase = domainCustomPrefix ? `${domainCustomPrefix}/login` : '/login';
  1. 在 Vue Router 中使用该路径
// main.ts
createRouter({
  history: createWebHistory(routerBase),
  routes: [...]
});

原理解析

  1. createWebHistory 特性
  • createWebHistory(base) 会把 base 参数作为所有路由的基础路径
  • Vue Router 会自动处理这个基础路径,不会重复添加;
    • .e.g.,当前 routerbase 为 /miniapp/login,访问路domain.com/miniapp/login时,- Vue Router 发现当前 URL 已经包含了这个 base,所以不会重复添加,而是把它作为基础路径,后续的路由都会基于这个基础路径
  1. 为什么之前用 Memory 模式没问题
  • 路由完全基于内存,不会修改浏览器的 URL
  • 当使用 location.href 跳转时,完全是一个新的导航,不会被 Vue Router 处理

小结

  1. 使用 HTML5 模式时,注意 base 路径配置
  2. 避免硬编码路径,使用动态配置
  3. 确保访问地址与 routeBase 一致