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事件(用户点击浏览器的前进/后退按钮时触发),来拦截并渲染对应组件。
- 基于 HTML5 History API:
致命问题与解决(面试必问):
- 问题:在 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;
}- 问题:在 History 模式下,如果在
2. 导航守卫 (Navigation Guards)
面试题:说说 Vue Router 的导航守卫有哪些?完整的解析流程是怎样的?
导航守卫用于在路由跳转的过程中进行权限校验、页面取消等操作。
守卫分类
- 全局守卫:
router.beforeEach((to, from, next)):全局前置守卫,最常用,通常在这里做登录态(Token)的拦截与校验。router.beforeResolve:全局解析守卫,在组件内守卫和异步路由组件被解析之后调用。router.afterEach((to, from)):全局后置钩子,没有next,通常用于页面跳转后修改document.title或发送分析数据(如 PV 统计)。
- 路由独享守卫:
beforeEnter:直接在路由配置表里定义,只对该特定路由生效。
- 组件内守卫:
beforeRouteEnter:在渲染该组件的对应路由被验证前调用。注意:这里拿不到组件实例this,因为组件还没被创建。如果非要操作实例,可以通过next(vm => { ... })的回调。beforeRouteUpdate:当前路由改变,但是该组件被复用时调用(比如动态路由/user/1跳转到/user/2)。beforeRouteLeave:导航离开该组件的对应路由时调用。常用于禁止用户在还未保存草稿时突然离开,或者清除定时器。
完整的导航解析流程
当用户从路由 A 导航到路由 B 时,完整的守卫触发顺序如下:
- 导航被触发。
- 在失活的组件(A)里调用
beforeRouteLeave守卫。 - 调用全局的
beforeEach守卫。 - 在重用的组件里调用
beforeRouteUpdate守卫(如果适用)。 - 在路由配置里调用
beforeEnter。 - 解析异步路由组件。
- 在被激活的组件(B)里调用
beforeRouteEnter。 - 调用全局的
beforeResolve守卫。 - 导航被确认。
- 调用全局的
afterEach钩子。 - 触发 DOM 更新 (挂载 B 组件)。
- 调用
beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调方法的参数传入。
3. 动态路由与路由传参
Params vs Query
- Query 传参:
- 类似 GET 请求,参数拼接在 URL 后面:
/user?id=123。 - 通过
route.query.id获取。 - 刷新页面参数不会丢失。
- 类似 GET 请求,参数拼接在 URL 后面:
- 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。
- 如果是路径参数(