路由守卫是前端单页应用中用于控制路由跳转逻辑的功能,常用于权限校验、登录状态判断、页面跳转前置处理等场景。在Vue、React等框架中通常有内置的路由守卫能力,但理解原生JavaScript实现路由守卫的思路,能帮助我们更清晰地掌握路由控制的底层逻辑。

路由守卫的核心需求
一个基础的路由守卫需要实现以下几个核心能力:
- 监听路由的变化,包括页面加载时的初始路由和后续的路由跳转
- 支持注册多个守卫函数,每个守卫可以决定是否允许路由跳转
- 跳转前执行所有守卫逻辑,若任一守卫拒绝跳转则终止流程
- 支持路由跳转时传递参数,守卫可以获取跳转相关的信息
实现思路拆解
1. 监听路由变化
前端路由通常分为hash模式和history模式,这里以更常用的hash模式为例,通过监听hashchange事件和页面加载时的load事件来捕获路由变化。
2. 路由匹配与守卫注册
我们需要维护一个路由配置表,存储路由路径和对应的组件或者处理逻辑,同时提供一个注册守卫的方法,允许开发者添加自定义的守卫函数。
3. 守卫执行逻辑
当路由发生变化时,先依次执行所有注册的守卫函数,每个守卫可以返回true表示允许跳转,返回false表示拒绝跳转,也可以通过返回Promise支持异步守卫逻辑。
完整实现代码
以下是基于hash模式的路由守卫完整实现示例:
// 路由管理器类
class RouterGuard {
constructor() {
// 存储路由配置
this.routes = {};
// 存储守卫函数列表
this.guards = [];
// 当前路由路径
this.currentRoute = null;
// 初始化监听
this.initListener();
}
// 初始化路由监听
initListener() {
// 页面加载时处理初始路由
window.addEventListener('load', () => {
this.handleRouteChange(window.location.hash.slice(1) || '/');
});
// hash变化时处理路由
window.addEventListener('hashchange', (e) => {
const newHash = window.location.hash.slice(1) || '/';
this.handleRouteChange(newHash);
});
}
// 注册路由
registerRoute(path, callback) {
this.routes[path] = callback;
}
// 添加路由守卫
addGuard(guardFn) {
this.guards.push(guardFn);
}
// 处理路由变化
async handleRouteChange(targetPath) {
// 构造路由上下文对象
const context = {
from: this.currentRoute,
to: targetPath,
hash: window.location.hash
};
// 依次执行所有守卫
for (const guard of this.guards) {
const result = await guard(context);
// 若守卫返回false,终止跳转
if (result === false) {
console.log('路由跳转被守卫拦截');
return;
}
}
// 执行路由对应的回调
const routeCallback = this.routes[targetPath];
if (routeCallback) {
routeCallback(context);
this.currentRoute = targetPath;
} else {
console.log('未找到对应路由');
}
}
// 主动跳转路由
push(path) {
window.location.hash = path;
}
}
// 使用示例
const router = new RouterGuard();
// 注册路由
router.registerRoute('/', (context) => {
console.log('进入首页', context);
document.getElementById('app').innerHTML = '首页内容';
});
router.registerRoute('/user', (context) => {
console.log('进入用户页', context);
document.getElementById('app').innerHTML = '用户页内容';
});
router.registerRoute('/admin', (context) => {
console.log('进入管理页', context);
document.getElementById('app').innerHTML = '管理页内容';
});
// 添加登录状态校验守卫
router.addGuard(async (context) => {
console.log('执行登录校验守卫');
// 模拟获取登录状态,实际项目中从本地存储或接口获取
const isLogin = localStorage.getItem('isLogin') === 'true';
// 访问需要权限的页面时校验登录状态
if (context.to === '/admin' && !isLogin) {
alert('请先登录');
router.push('/');
return false;
}
return true;
});
// 添加跳转日志守卫
router.addGuard((context) => {
console.log(`路由从 ${context.from || '无'} 跳转到 ${context.to}`);
return true;
});
// 页面中模拟登录按钮
document.getElementById('loginBtn').addEventListener('click', () => {
localStorage.setItem('isLogin', 'true');
alert('登录成功');
});
document.getElementById('logoutBtn').addEventListener('click', () => {
localStorage.setItem('isLogin', 'false');
alert('退出成功');
});
代码说明
上述代码中,RouterGuard类封装了路由守卫的核心逻辑:
routes对象存储路由路径和对应的处理逻辑,registerRoute方法用于添加路由配置guards数组存储所有注册的守卫函数,addGuard方法用于添加守卫handleRouteChange方法是核心处理逻辑,会先执行所有守卫,全部通过后才执行路由对应的回调- 守卫函数支持异步,通过
await执行,方便处理接口校验等异步场景
使用时只需要实例化RouterGuard,注册路由和守卫,就可以实现自定义的路由拦截逻辑。如果需要适配history模式,只需要把hashchange监听替换为popstate监听,同时跳转时使用history.pushState方法即可,核心守卫逻辑不需要改动。
JavaScript路由守卫前端路由路由拦截修改时间:2026-06-30 00:09:32