【拿来就用】Uniapp路由守卫终极方案:1个文件搞定全站权限控制,老板看了都点赞!

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: 拦截器的性能开销很小。如果担心性能,可以:

  1. 只在开发环境启用详细日志

  2. 对频繁访问的白名单页面做缓存

  3. 避免在拦截器中执行复杂操作

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 的路由守卫功能。关键点:

  1. 拦截四个路由 APInavigateToredirectToreLaunchswitchTab

  2. 灵活的白名单配置:支持动态更新

  3. 完善的错误处理:跳转失败时有降级方案

  4. 可扩展性强:支持角色权限、Token刷新等高级功能

这套方案已经过多个项目的验证,可以直接复制使用。根据实际需求,调整白名单和权限校验逻辑即可。

立即使用 :将上面的 route-guard.js 复制到你的项目中,然后在 App.vueonLaunch 中调用 initRouteGuard(),你的 Uniapp 应用就拥有了强大的路由守卫功能!

相关推荐
C雨后彩虹15 小时前
任务最优调度
java·数据结构·算法·华为·面试
智航GIS17 小时前
10.4 Selenium:Web 自动化测试框架
前端·python·selenium·测试工具
前端工作日常17 小时前
我学习到的A2UI概念
前端
徐同保18 小时前
为什么修改 .gitignore 后还能提交
前端
一只小bit18 小时前
Qt 常用控件详解:按钮类 / 显示类 / 输入类属性、信号与实战示例
前端·c++·qt·gui
Mr -老鬼18 小时前
前端静态路由与动态路由:全维度总结与实践指南
前端
Nan_Shu_61419 小时前
学习: Threejs (1)
javascript·学习
颜酱19 小时前
前端必备动态规划的10道经典题目
前端·后端·算法
Chan1619 小时前
【 Java八股文面试 | JavaSE篇 】
java·jvm·spring boot·面试·java-ee·八股
wen__xvn19 小时前
代码随想录算法训练营DAY10第五章 栈与队列part01
java·前端·算法