Skip to main content

Vue Router

在 Vue 面试中,Vue Router 是不可或缺的考察点,主要集中在路由模式的底层原理和导航守卫的执行机制上。

1. 路由模式:Hash vs History

面试必考:说说前端路由的两种模式,以及它们的底层原理是什么?

单页应用(SPA)的核心在于:改变 URL,但不向服务器重新发起整个页面的请求,同时利用 JS 监听到 URL 的变化,去渲染对应的组件。

Hash 模式 (默认)

  • 表现形式:URL 中带有一个 # 号(例如 http://www.abc.com/#/home)。
  • 底层原理
    • 基于 location.hash 来获取或修改当前的 hash 值。
    • 通过监听 hashchange 事件,在回调函数中根据不同的 hash 值,渲染不同的组件。
  • 优点:兼容性极好(支持低版本浏览器);不需要服务器端做任何额外的配置,因为 # 后面的内容不会被发送到服务器。
  • 缺点:URL 带有 #,看起来不美观;在某些特定的 App 内嵌 Webview 中可能会遇到奇怪的缓存或分享问题。

History 模式

  • 表现形式:普通的 URL,没有 # 号(例如 http://www.abc.com/home)。

  • 底层原理

    • 基于 HTML5 History API:history.pushState()history.replaceState()。这两个 API 可以在不刷新页面的情况下,动态修改浏览器的 URL。
    • 通过监听 popstate 事件(用户点击浏览器的前进/后退按钮时触发),来拦截并渲染对应组件。
  • 致命问题与解决(面试必问)

    • 问题:在 History 模式下,如果在 http://www.abc.com/home 直接刷新页面,浏览器会真正向服务器发起对 /home 这个路径的 GET 请求。由于这是一个单页应用,服务器并没有 /home 这个真实的物理目录或文件,因此会返回 404 Not Found
    • 解决方案:必须在服务器端(如 Nginx, Apache)配置兜底(Fallback)规则。即:如果 URL 匹配不到任何静态资源,就重定向返回 index.html 页面。然后由前端 Vue Router 接管路由逻辑,渲染出对应的组件。

    Nginx 配置示例:

    location / {
    try_files $uri $uri/ /index.html;
    }

2. 导航守卫 (Navigation Guards)

面试题:说说 Vue Router 的导航守卫有哪些?完整的解析流程是怎样的?

导航守卫用于在路由跳转的过程中进行权限校验、页面取消等操作。

守卫分类

  1. 全局守卫
    • router.beforeEach((to, from, next)):全局前置守卫,最常用,通常在这里做登录态(Token)的拦截与校验。
    • router.beforeResolve:全局解析守卫,在组件内守卫和异步路由组件被解析之后调用。
    • router.afterEach((to, from)):全局后置钩子,没有 next,通常用于页面跳转后修改 document.title 或发送分析数据(如 PV 统计)。
  2. 路由独享守卫
    • beforeEnter:直接在路由配置表里定义,只对该特定路由生效。
  3. 组件内守卫
    • beforeRouteEnter:在渲染该组件的对应路由被验证前调用。注意:这里拿不到组件实例 this,因为组件还没被创建。如果非要操作实例,可以通过 next(vm => { ... }) 的回调。
    • beforeRouteUpdate:当前路由改变,但是该组件被复用时调用(比如动态路由 /user/1 跳转到 /user/2)。
    • beforeRouteLeave:导航离开该组件的对应路由时调用。常用于禁止用户在还未保存草稿时突然离开,或者清除定时器。

完整的导航解析流程

当用户从路由 A 导航到路由 B 时,完整的守卫触发顺序如下:

  1. 导航被触发。
  2. 在失活的组件(A)里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫(如果适用)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件(B)里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新 (挂载 B 组件)。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调方法的参数传入。

3. 动态路由与路由传参

Params vs Query

  1. Query 传参
    • 类似 GET 请求,参数拼接在 URL 后面:/user?id=123
    • 通过 route.query.id 获取。
    • 刷新页面参数不会丢失
  2. Params 传参
    • 如果是路径参数(/user/:id),则属于路径的一部分:/user/123。通过 route.params.id 获取,刷新页面不会丢失
    • 注意(Vue Router 4 的重大改动):如果使用的是隐式 Params 传参(即配置了 name 但没有在 path 中定义对应的动态字段,通过 { name: 'User', params: { id: 123 } } 传参),在 Vue Router 4 中已经被彻底移除并废弃。因为这种隐式参数会在页面刷新时完全丢失,违背了 URL 是页面状态唯一真实来源的原则。现在推荐使用 query,或者将参数存入状态管理(Pinia)/ sessionStorage