js怎样实现路由跳转拦截 js路由跳转拦截的5种处理方案

js怎样实现路由跳转拦截 js路由跳转拦截的5种处理方案
最新回答
鲜奶千层雪

2022-11-04 17:25:52

JS实现路由跳转拦截的5种处理方案如下

  • 方案一:利用beforeunload事件通过监听beforeunload事件,在页面关闭、刷新或跳转前弹出确认对话框,阻止用户离开当前页面。适用于全局页面离开提示,但无法自定义界面且无法区分操作类型。示例代码如下:
window.addEventListener('beforeunload', function (e) { e = e || window.event; if (/* 判断条件,如表单未保存 */ true) { e.preventDefault(); e.returnValue = '您确定要离开此页面吗?未保存的更改将会丢失。'; return '您确定要离开此页面吗?未保存的更改将会丢失。'; }});

优点:简单粗暴,兼容性好。缺点:用户体验差,无法区分刷新或跳转,拦截所有离开操作。

  • 方案二:使用hashchange事件(适用于hash路由)监听hashchange事件,在hash值改变前拦截跳转。适用于hash路由,但对history路由无效。示例代码如下:
window.addEventListener('hashchange', function (e) { const oldURL = e.oldURL; const newURL = e.newURL; if (/* 判断条件,如权限不足 */ true) { window.location.hash = oldURL.split('#')[1]; // 改回原hash值 alert('权限不足,无法访问该页面'); // 或显示提示信息 }});

优点:精确控制hash路由跳转。缺点:仅适用于hash路由,直接修改window.location.hash可能导致历史记录混乱。

  • 方案三:使用popstate事件(适用于history路由)监听popstate事件,在浏览器前进/后退或调用history.go()等方法时触发拦截。适用于history路由,但不会响应pushState/replaceState操作。示例代码如下:
window.addEventListener('popstate', function (e) { if (/* 判断条件,如未登录 */ true) { history.pushState(null, null, e.target.location.pathname); // 改回原状态 window.location.href = '/login'; // 或重定向到登录页 }});

注意:需手动处理pushState/replaceState操作,因其不会触发popstate事件。

  • 方案四:Vue Router的beforeEach导航守卫在Vue项目中集成全局路由控制,便于权限验证和跳转管理。示例代码如下:
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth) { // 判断目标路由是否需要登录 if (localStorage.getItem('token')) { next(); // 已登录,继续跳转 } else { next('/login'); // 未登录,跳转到登录页 } } else { next(); // 不需要登录,继续跳转 }});

优点:简单易用,与Vue生态完美集成。缺点:仅适用于Vue项目。

  • 方案五:React Router的Prompt组件或useBlocker Hook在React项目中实现灵活的跳转提示与控制。使用Prompt组件
import { Prompt } from 'react-router-dom';function MyComponent() { const [isDirty, setIsDirty] = useState(false); return ( <div> <Prompt when={isDirty} message="您确定要离开此页面吗?未保存的更改将会丢失。" /> {/* 组件内容 */} </div> );}

使用自定义Hook(基于useBlocker)

import { useEffect, useRef } from 'react';import { unstable_useBlocker as useBlocker } from 'react-router-dom';function usePreventNavigation(isDirty, message = '您确定要离开此页面吗?未保存的更改将会丢失。') { const blocker = useBlocker(isDirty); useEffect(() => { if (blocker.state === 'blocked') { const confirmation = window.confirm(message); if (confirmation) { blocker.proceed(); } else { blocker.reset(); } } }, [blocker, message]); return blocker;}function MyComponent() { const [isDirty, setIsDirty] = useState(false); const blocker = usePreventNavigation(isDirty); return <div>{/* 组件内容 */}</div>;}

注意:useBlocker为实验性API,未来版本可能变更。

拦截中处理异步逻辑通过async/await或Promise实现异步操作(如权限验证、数据加载)的拦截控制。示例(Vue Router):

router.beforeEach(async (to, from, next) => { if (to.meta.requiresAuth) { try { const isLoggedIn = await checkLoginStatus(); // 异步检查登录状态 isLoggedIn ? next() : next('/login'); } catch (error) { console.error('检查登录状态失败', error); next('/error'); // 导航到错误页 } } else { next(); }});async function checkLoginStatus() { return new Promise((resolve) => { setTimeout(() => { const token = localStorage.getItem('token'); resolve(!!token); }, 500); });}

避免死循环的实践

  • 设置导航标志位:通过全局变量标记导航状态,防止重复拦截。
let isNavigating = false;router.beforeEach((to, from, next) => { if (isNavigating) return next(); if (to.meta.requiresAuth && !localStorage.getItem('token')) { isNavigating = true; next('/login'); setTimeout(() => { isNavigating = false; }, 100); // 延迟重置 } else { next(); }});
  • 维护白名单列表:允许特定路径(如登录页)无条件通过。
const whiteList = ['/login', '/register'];router.beforeEach((to, from, next) => { if (whiteList.includes(to.path)) return next(); to.meta.requiresAuth && !localStorage.getItem('token') ? next('/login') : next();});

最佳实践总结

  • 仅在必要时拦截,避免过度干扰用户。
  • 提供明确提示信息,说明拦截原因及解决方案。
  • 通过标志位或白名单避免死循环。
  • 合理处理异步操作,确保逻辑完整性。
  • 优先使用框架提供的API(如Vue Router/React Router)。
  • 谨慎使用beforeunload,仅在需要提醒保存未保存数据时使用。
  • 充分测试拦截逻辑,确保功能与用户体验兼顾。