前端路由守卫与异步请求的竞态问题
背景介绍
业务场景
在开发企业级 Web 平台时,通常需要实现严格的授权控制。在我们的 MSS-Portal 项目中,有这样一个典型场景:
功能需求
授权检查:用户访问平台时,调用
check_agree接口验证授权状态区域化处理:
- 国内用户:展示授权协议 HTML 内容
- 海外用户:展示服务经理信息,提醒邮件激活
实现方式:所有检查逻辑在路由守卫中完成
问题分析
问题现象
在开发过程中发现,授权检查请求总是被意外中断,具体表现为:
- F12 开发者工具显示请求被取消
- 后端 Nginx 未记录到请求日志
- 控制台报错:“Request cancelled due to route jump”

问题定位
通过在 axios 拦截器中添加日志,定位到关键代码:
// 路由守卫代码
if (!store?.state?.auth?.isAgreedAgreement) {
customerPerimissiomShowDialog(); // async 函数但未使用 await
}
// axios 配置
if (!GLOBAL_AJAX_URL.includes(config.url || '')) {
// 请求被配置了中断处理
config.cancelToken = new Axios.CancelToken((cancel: Canceler) => {});
}
// 授权弹窗的调用实现
export async function customerPerimissiomShowDialog() {
}技术原理深入解析
JavaScript 事件循环机制
// 执行顺序示意
async function routerGuard(to, from, next) {
console.log('1. 进入守卫'); // 同步代码
if (!isAuthorized) {
console.log('2. 检查授权'); // 同步代码
customerPerimissiomShowDialog(); // 异步操作
console.log('3. 发起请求'); // 同步代码
}
console.log('4. 继续路由'); // 同步代码
next(); // 触发路由跳转
// 5. 此时异步请求才开始执行,但路由已改变
}事件循环中的任务优先级
同步任务:直接在主线程执行
微任务(Microtasks):
- Promise 回调
- Process.nextTick
- MutationObserver
宏任务(Macrotasks):
- setTimeout/setInterval
- requestAnimationFrame
- I/O 操作
解决方案
- 使用 await 控制执行顺序
if (!store?.state?.auth?.isAgreedAgreement) {
await customerPerimissiomShowDialog(); // async 函数但未使用 await
}- 请求白名单配置
// axios.config.ts
const GLOBAL_AJAX_URL = [
'/api/v1/auth/check_agree',
'/api/v1/auth/agreement_content',
'/api/v1/auth/manager_info'
];
// axios 拦截器
axios.interceptors.request.use(config => {
if (!GLOBAL_AJAX_URL.includes(config.url || '')) {
const source = axios.CancelToken.source();
config.cancelToken = source.token;
// 存储取消令牌
store.commit('addCancelToken', { url: config.url, token: source });
}
return config;
});- 独立 Axios 实例
// auth.axios.ts
export const authAxios = axios.create({
baseURL: process.env.VUE_APP_AUTH_API,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// 不添加取消令牌的拦截器
authAxios.interceptors.request.use(config => {
// 仅添加认证等基础配置
return config;
});经验总结
异步控制
- 在路由守卫中使用异步操作时,必须使用 await 等待完成
- 理解 JavaScript 事件循环机制对于处理异步问题至关重要
请求管理
- 合理使用请求取消机制,避免无效请求
- 对关键请求实现白名单保护
- 考虑使用独立的请求实例处理特殊场景
性能优化
- 实现合理的缓存策略
- 使用请求防抖避免重复请求
- 适当预加载提升用户体验
错误处理
- 完善的错误处理机制
- 降级策略的实现
- 用户友好的错误提示
这个案例很好地展示了前端异步编程中的常见陷阱,以及如何通过合理的编程实践来避免这些问题。在实际开发中,我们要始终记住 JavaScript 的异步特性,合理使用 async/await,这样才能写出更可靠的代码。