Uniapp 开发H5 页面跳转拦截

路由拦截器的核心思想是 在页面跳转之前插入一道"关卡",统一判断是否放行

plantext 复制代码
用户点击按钮
      │
      ▼
  ┌─────────────┐
  │  invoke 拦截  │  ← 在这里做登录校验、权限判断、参数清洗
  └──────┬───────┘
         │
    ┌────┴────┐
    ▼         ▼
 return true  return false
    │         │
    ▼         ▼
 正常跳转    阻断跳转(可提示/重定向)

代码实现:uni.addInterceptor 文档:https://uniapp.dcloud.net.cn/api/interceptor.html

js 复制代码
uni.addInterceptor(method, {
  // 调用前拦截 ------ 返回 false 可阻断跳转
  invoke(args) {
    // args.url  = '/pages/index/index?id=1'
    // args.events = { ... }   // Vue3 的事件通信
    return true // true 放行,false 阻断
  },

  // 成功回调拦截
  success(result) {
    // 可在此修改回调参数
    return result
  },

  // 失败回调拦截
  fail(err) {
    console.error(err)
    return err
  },

  // 完成回调拦截(无论成功失败)
  complete(result) {
    return result
  }
})

可拦截的路由方法(共 5 个)

方法 行为 被拦截
uni.navigateTo 保留当前页,跳转新页面(可返回)
uni.redirectTo 关闭当前页,跳转新页面(不可返回)
uni.reLaunch 关闭所有页面,打开新页面
uni.switchTab 跳转 TabBar 页面
uni.navigateBack 返回上一页 否(无法拦截)

完整路由拦截代码实现

js 复制代码
/**
 * uni-app 路由拦截器
 *
 * 在 main.js 中 new Vue() 之前引入:
 *   import '@/common/routerInterceptor.js'
 *
 * 功能:
 *   1. 模块加载时对 H5 首屏做登录校验
 *   2. 拦截 navigateTo / redirectTo / reLaunch / switchTab,未登录一律跳登录页
 *   3. 跨 Tab 页 token 被清除时自动同步(仅 H5)
 */

import store from '@/store/index.js'

// 常量
//登录页面path
const LOGIN_PAGE = '/pages/login/login'
// 日志头
const LOG_TAG = '[RouterInterceptor]'

/** 免登录白名单 */
const whiteSet = new Set([
  '/pages/login/login',
  '/pages/test/test2',
  '/pages/test/test',
  // 按需追加 ...
])


/** 当前运行平台是否为 H5 */
// VUE_APP_PLATFORM 避免使用uniapp cli 构建时无此变量
// const isH5 = process.env.VUE_APP_PLATFORM === 'h5'
// #ifdef H5
const isH5 = true
// #endif
// #ifndef H5
const isH5 = false
// #endif

/** 从 store 中实时读取 token */
const getToken = () => {
  //localStorage.getItem('token')
  return store.getters.token
}

/**
 * 从完整 url 中提取页面路径(去掉 ? 及后面的参数)
 * @param {string} url - e.g. '/pages/index/index?id=1'
 * @returns {string}     e.g. '/pages/index/index'
 */
function extractPath(url) {
  return url.split('?')[0]
}

/**
 * 判断页面路径是否需要鉴权
 * @param {string} path
 * @returns {boolean}
 */
function requiresAuth(path) {
  return !whiteSet.has(path)
}

  /**
    * 获取当前路由路径(处理 hash / history 模式)
    * - hash 模式:返回 hash 中的路径部分(不含 # 和查询参数)
    * - history 模式:返回 pathname
    * @returns {string} 例如 "/user/detail"
    */
function getCurrentRoute() {
	const hash = location.hash;
      // hash 以 #/ 开头视为 hash 路由(#/path)
      if (hash && hash.charAt(0) === '#' && hash.charAt(1) === '/') {
        // #/user/detail?id=1 => /user/detail
        const hash = location.hash.slice(1); // 去掉 #
        const qIdx = hash.indexOf('?');
        const pIdx = hash.indexOf('#'); // hash 内嵌锚点
        let end = hash.length;
        if (qIdx !== -1) end = Math.min(end, qIdx);
        if (pIdx !== -1) end = Math.min(end, pIdx);
        return hash.slice(0, end) || '/';
      }
    return location.pathname || '/';
}

/**
 * 跳转到登录页
 * - 防止已在登录页时重复跳转
 * - toast 提示后延时跳转,让用户能看到提示
 */
function jumpToLogin() {
  const pages = getCurrentPages()
  const currentPage = pages.length ? `/${pages[pages.length - 1].route}` : ''
  console.log(LOG_TAG, `jumpToLogin, current: ${currentPage}`)

  if (currentPage === LOGIN_PAGE) return

  uni.showToast({ title: '请先登录', icon: 'none', duration: 1500 })
  setTimeout(() => {
    uni.redirectTo({ url: LOGIN_PAGE })
  }, 500)
}

// 模块加载时立即执行一次,处理用户直接通过地址栏访问的场景
;
(function initialGuard() {
  const token = getToken()
  const currentPath = getCurrentRoute()
  console.log(LOG_TAG, `initial path = ${currentPath}, hasToken = ${!!token}`)

  if (!token && requiresAuth(currentPath)) {
    jumpToLogin()
  }
})()

// 路由方法拦截
const METHODS = ['navigateTo', 'redirectTo', 'reLaunch', 'switchTab']

METHODS.forEach((method) => {
  uni.addInterceptor(method, {
    invoke(args) {
      const path = extractPath(args.url)
      const token = getToken()

      console.log(LOG_TAG, `${method} → ${path}, hasToken = ${!!token}`)

      if (!token && requiresAuth(path)) {
        jumpToLogin()
        return false // 阻断本次跳转
      }
      return true
    },

    fail(err) {
      console.error(LOG_TAG, `${method} failed:`, err)
    },
  })
})

// 跨 Tab 页 token 同步(仅 H5)
// 可以在另一个 Tab 页退出登录,通过 localStorage 事件通知本页清除 token

if (isH5 && typeof window !== 'undefined') {
  window.addEventListener('storage', (e) => {
    if (e.key === 'token' && !e.newValue) {
      console.log(LOG_TAG, 'token removed in another tab, syncing')
      store.commit('SET_TOKEN', '')
    }
  })
}

跳转流程如下:

plantext 复制代码
uni.navigateTo({ url: '/pages/profile/profile?id=1' })
        │
        ▼
  invoke(args) 触发
        │
        ▼
  extractPath(args.url) → '/pages/profile/profile'
        │
        ▼
  getToken() 有值吗?
        │
   ┌────┴────┐
   Yes       No
   │         │
   ▼         ▼
 在白名单?  在白名单?
   │         │
  ┌┴┐       ┌┴┐
  No Yes    No Yes
  │   │     │   │
  ▼   ▼     ▼   ▼
 ✗   ✓     ✗   ✓
 │   │     │   │
 │   │     │   └→ 放行 return true
 │   └→ 放行 return true
 │
 ▼
 return true (有 token 就直接放行,不看白名单)


getToken() = null, 不在白名单:
        │
        ▼
  redirectToLogin('/pages/profile/profile')
        │
        ▼
  showToast('请先登录')
        │
        ▼ (1500ms)
  uni.redirectTo({
    url: '/pages/login/login?redirect=%2Fpages%2Fprofile%2Fprofile'
  })

这里token 过期交给服务处理,可以使用 请求拦截器(axios / uni.request)的响应拦截统一处理:

js 复制代码
// 响应拦截器(401 处理)
uni.addInterceptor('request', {
  success(res) {
    if (res.statusCode === 401) {
      store.commit('SET_TOKEN', '')
      redirectToLogin()
    }
    return res
  }
})
相关推荐
IT爱学堂1 小时前
Three.js可视化企业实战WEBGL课,2023年全新WEB 3D THREEJS技术
前端·javascript·webgl
网安蟹佬霸1 小时前
WebAssembly安全攻防实战:从WASM逆向到漏洞利用
前端·安全·自动化·区块链·智能合约·wasm
Su米苏1 小时前
关于@vue-office/excel 渲染异常滚动失去内容
前端·vue.js·excel
灯澜忆梦1 小时前
【基于GO的Web开发10】gin获取HTML-Form表单提交参数
前端·后端·golang·html·gin
撑伞的鱼99372 小时前
2026年前端AI编程工具评测:Figma 还原、组件复用、跨文件联动三项对比
前端·ai编程·figma·效率工具·ai编程工具
码视野2 小时前
基于 Vue3 + Element Plus 的【微短剧剧本智能创作与分镜生产协同系统】设计与实现(含PRD/三端源码/大屏)
前端·人工智能·vue3
zzzzzz3102 小时前
36K stars 的“酷炫组件”,到底该怎么用才不显得用力过猛?
前端·react.js·动效
2401_894915535 小时前
GEO 优化源码全解析:从搜索引擎到 AI 引擎的底层改写逻辑
java·服务器·前端·数据库·人工智能·分布式·搜索引擎
rockey62710 小时前
C#脚本引擎之AScript与Jurassic、Jint对比
javascript·c#·.net·js·script·动态脚本