1. 前言:从 Vue-Router 到 Uniapp
在 Vue 项目中,我们使用 Vue-Router 的 beforeEach 导航守卫轻松实现路由拦截、权限校验等功能:
php
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', redirect: '/login' }
]
})
// Vue-Router 导航守卫
router.beforeEach((to, from, next) => {
if (to.path === '/login') return next()
const token = sessionStorage.getItem('token')
if (!token) {
return next('/login')
}
next()
})
但 Uniapp 的情况不同 :作为一个跨端框架,Uniapp 没有内置类似 Vue-Router 的路由守卫机制。不过别担心,我们可以通过 Uniapp 的拦截器 API 实现相同的功能!
2. Uniapp 路由拦截器:核心概念
2.1 什么是拦截器?
Uniapp 提供了 uni.addInterceptor API,允许我们拦截特定的 API 调用。官方文档:拦截器
2.2 需要拦截哪些 API?
要实现路由守卫,我们需要拦截以下四个页面跳转 API:
-
uni.navigateTo// 保留当前页面,跳转到新页面 -
uni.redirectTo// 关闭当前页面,跳转到新页面 -
uni.reLaunch// 关闭所有页面,打开新页面 -
uni.switchTab// 跳转到 TabBar 页面
2.3 拦截器的核心:invoke 方法
每个拦截器都需要实现 invoke 方法,它会在对应的 API 调用时触发,让我们有机会在跳转前进行校验。
3. 实战:创建路由守卫工具类
在 utils 目录下创建 route-guard.js:
javascript
/**
* 名称:全局路由守卫工具类
* 功能:实现页面跳转的权限校验(类似 Vue-Router 的 beforeEach)
* 使用:在 App.vue 的 onLaunch 中调用 initRouteGuard()
*/
// ==================== 配置部分 ====================
const routeConfig = {
// 白名单:无需登录即可访问的页面路径
whiteList: new Set([
'/pages/login/login',
'/pages/register/register',
'/pages/index/index',
'/pages/public/public-page'
]),
// 登录页路径
loginPage: '/pages/login/login',
// Token 存储的 key
tokenKey: 'token'
};
// ==================== 核心校验逻辑 ====================
/**
* 检查路由权限
* @param {string} url - 要跳转的页面路径
* @returns {boolean} - 是否允许跳转
*/
const checkRoutePermission = (url) => {
// 提取路径(去除查询参数)
const path = url.split('?')[0];
// 放行白名单
if (routeConfig.whiteList.has(path)) {
return true;
}
// 检查 Token
const token = uni.getStorageSync(routeConfig.tokenKey);
// 双重验证:存在且有效(这里可以添加更多验证逻辑)
if (token && typeof token === 'string' && token.length > 0) {
// 可以在这里添加 Token 过期验证
// const isExpired = checkTokenExpiry(token);
// return !isExpired;
return true;
}
return false;
};
// ==================== 拦截器配置 ====================
const routeInterceptor = {
/**
* 拦截器核心方法
* @param {Object} args - 原始 API 参数
* @returns {boolean} - 是否继续执行原始 API
*/
invoke(args) {
console.log(`🚦 路由拦截: ${args.url}`);
// 检查权限
if (checkRoutePermission(args.url)) {
return true; // 放行,继续执行原始跳转
}
// 无权限:跳转到登录页
console.warn('❌ 无访问权限,跳转到登录页');
// 携带 redirect 参数,登录后可以跳回原页面
const redirectUrl = encodeURIComponent(args.url);
uni.redirectTo({
url: `${routeConfig.loginPage}?redirect=${redirectUrl}`,
fail: (error) => {
console.error('跳转登录页失败:', error);
}
});
return false; // 阻止原始跳转
}
};
// ==================== 初始化方法 ====================
/**
* 初始化路由守卫
*/
export const initRouteGuard = () => {
try {
// 拦截所有页面跳转 API
uni.addInterceptor('navigateTo', routeInterceptor);
uni.addInterceptor('redirectTo', routeInterceptor);
uni.addInterceptor('reLaunch', routeInterceptor);
uni.addInterceptor('switchTab', routeInterceptor);
console.log('✅ 路由守卫已启用');
} catch (error) {
console.error('❌ 路由守卫初始化失败:', error);
}
};
/**
* 移除路由守卫(特殊场景使用)
*/
export const removeRouteGuard = () => {
try {
uni.removeInterceptor('navigateTo');
uni.removeInterceptor('redirectTo');
uni.removeInterceptor('reLaunch');
uni.removeInterceptor('switchTab');
console.log('✅ 路由守卫已移除');
} catch (error) {
console.error('❌ 移除路由守卫失败:', error);
}
};
// ==================== 辅助方法 ====================
/**
* 更新白名单(动态配置)
* @param {Array} newRoutes - 新的白名单路由数组
*/
export const updateWhiteList = (newRoutes) => {
routeConfig.whiteList = new Set(newRoutes);
};
/**
* 检查当前页面是否在白名单
* @param {string} path - 页面路径
* @returns {boolean}
*/
export const isInWhiteList = (path) => {
return routeConfig.whiteList.has(path);
};
4. 使用指南
4.1 基础使用
在 App.vue 中初始化:
xml
<script>
import { initRouteGuard } from '@/utils/route-guard.js';
export default {
onLaunch() {
console.log('App Launch');
// 初始化路由守卫(必须在页面跳转前调用)
initRouteGuard();
// 其他初始化代码...
},
onShow() {
console.log('App Show');
},
onHide() {
console.log('App Hide');
}
}
</script>
<style>
/* 全局样式 */
</style>
4.2 高级配置
ini
// 在需要的地方动态更新配置
import { updateWhiteList, isInWhiteList } from '@/utils/route-guard.js';
// 添加新的白名单路由
const additionalRoutes = [
'/pages/public/another-public',
'/pages/guest/guest-page'
];
updateWhiteList([...routeConfig.whiteList, ...additionalRoutes]);
// 检查当前页面权限
const currentPage = '/pages/user/profile';
if (!isInWhiteList(currentPage)) {
console.log('需要登录才能访问');
}
5. 实战场景扩展
场景1:不同用户角色的权限控制
kotlin
// 扩展 checkRoutePermission 函数
const checkRoutePermission = (url, userRole) => {
const path = url.split('?')[0];
// 公开页面
if (routeConfig.whiteList.has(path)) {
return true;
}
// 需要登录的页面
const token = uni.getStorageSync('token');
if (!token) return false;
// 根据角色检查权限
const role = uni.getStorageSync('userRole') || 'user';
// 管理员专属页面
if (path.startsWith('/pages/admin/') && role !== 'admin') {
return false;
}
// VIP 专属页面
if (path.startsWith('/pages/vip/') && role !== 'vip') {
return false;
}
return true;
};
场景2:Token 自动刷新
ini
const checkRoutePermission = async (url) => {
const path = url.split('?')[0];
if (routeConfig.whiteList.has(path)) {
return true;
}
let token = uni.getStorageSync('token');
const tokenExpiry = uni.getStorageSync('tokenExpiry');
// 检查 Token 是否过期
if (token && tokenExpiry && Date.now() > tokenExpiry) {
// 自动刷新 Token
const refreshed = await refreshToken();
if (!refreshed) {
return false;
}
token = uni.getStorageSync('token');
}
return !!token;
};
场景3:路由跳转前的统一处理
javascript
const routeInterceptor = {
invoke(args) {
// 统一添加时间戳参数(解决页面缓存问题)
if (!args.url.includes('timestamp')) {
args.url += `${args.url.includes('?') ? '&' : '?'}_t=${Date.now()}`;
}
// 页面访问统计
logPageAccess(args.url);
// 权限检查
if (checkRoutePermission(args.url)) {
return true;
}
// 无权限处理
return handleNoPermission(args.url);
}
};
6. 常见问题与解决方案
Q1: TabBar 页面拦截无效?
A : switchTab 跳转时,如果目标页面已在 TabBar 中显示,拦截可能不会触发。建议在页面的 onShow 生命周期中做二次验证。
javascript
// 在页面中
export default {
onShow() {
const token = uni.getStorageSync('token');
if (!token && !isInWhiteList(this.$page.route)) {
uni.redirectTo({
url: '/pages/login/login'
});
}
}
}
Q2: 拦截器影响性能?
A: 拦截器的性能开销很小。如果担心性能,可以:
-
只在开发环境启用详细日志
-
对频繁访问的白名单页面做缓存
-
避免在拦截器中执行复杂操作
Q3: 如何调试?
javascript
// 在 route-guard.js 中添加调试模式
const DEBUG = process.env.NODE_ENV === 'development';
const routeInterceptor = {
invoke(args) {
if (DEBUG) {
console.group('🔍 路由拦截详情');
console.log('目标URL:', args.url);
console.log('完整参数:', args);
console.groupEnd();
}
// ... 原有逻辑
}
};
7. 完整示例项目结构
bash
项目根目录/
├── pages/
│ ├── login/
│ │ └── login.vue
│ ├── home/
│ │ └── home.vue
│ └── user/
│ └── profile.vue
├── utils/
│ ├── route-guard.js # 路由守卫工具
│ ├── auth.js # 认证相关工具
│ └── request.js # 请求拦截器
├── App.vue # 初始化路由守卫
└── main.js # 应用入口
总结
通过 Uniapp 的拦截器机制,我们成功实现了类似 Vue-Router 的路由守卫功能。关键点:
-
拦截四个路由 API :
navigateTo、redirectTo、reLaunch、switchTab -
灵活的白名单配置:支持动态更新
-
完善的错误处理:跳转失败时有降级方案
-
可扩展性强:支持角色权限、Token刷新等高级功能
这套方案已经过多个项目的验证,可以直接复制使用。根据实际需求,调整白名单和权限校验逻辑即可。
立即使用 :将上面的 route-guard.js 复制到你的项目中,然后在 App.vue 的 onLaunch 中调用 initRouteGuard(),你的 Uniapp 应用就拥有了强大的路由守卫功能!