SvelteKit中beforeNavigate调用端点后cancel()无法取消路由问题
问题根因
beforeNavigate的取消逻辑仅支持同步触发:只要回调函数内存在await异步等待,SvelteKit不会等待异步逻辑执行完成,会直接推进导航流程。等你fetch拿到返回结果再调用cancel()时,导航已经执行完毕,自然无法拦截。你在await语句之前调用cancel()能生效,正是因为这些位置属于同步执行阶段,框架可以正常捕获取消信号。
实现方案
方案1:优先使用服务端Load函数做校验(推荐)
权限类的路由校验不要放在客户端beforeNavigate中处理,直接把校验逻辑迁移到对应路由(或根布局)的服务端load函数中即可。服务端load天然支持异步逻辑,执行完成前不会推进导航,校验不通过时直接抛出重定向或错误即可,不会出现拦截失效问题,也不需要额外处理循环跳转。
示例代码:
// 对应路由下的 +layout.server.js / +page.server.js export async function load({ url, fetch, redirect }) { const path = url.pathname.slice(1); const response = await fetch(`/api/check?path=${path}`, { method: 'GET' }); if ([401, 403].includes(response.status)) { // 校验不通过直接重定向,导航会被拦截 throw redirect(302, '/login?redirect=' + encodeURIComponent(url.pathname)); } // 校验通过正常返回页面数据 return {}; }
方案2:客户端拦截的正确写法
如果必须在客户端完成异步校验,需要调整逻辑:先同步取消默认导航,等异步校验通过后再手动执行跳转,同时增加标记避免循环触发导航拦截。
示例代码:
<!-- __layout.svelte --> <script> import { beforeNavigate, goto } from '$app/navigation'; // 标记校验通过后的手动跳转,避免无限拦截 let passCheckNavigate = false; beforeNavigate(({ to, cancel }) => { // 校验通过的跳转直接放行 if (passCheckNavigate) { passCheckNavigate = false; return; } // 第一步:同步取消当前默认导航,这一步必须在所有异步逻辑之前执行 cancel(); const targetPath = to?.pathname || ''; const path = targetPath.slice(1); // 执行异步校验 fetch(`/api/check?path=${path}`, { method: 'GET' }) .then(async (response) => { if ([401, 403].includes(response?.status)) { displayNotification('未授权'); return; } // 校验通过,打标记后手动跳转到目标路由 passCheckNavigate = true; await goto(to.url, { replaceState: true }); }) .catch(() => { displayNotification('操作异常,请稍后重试'); }); }); </script>
内容的提问来源于stack exchange,提问作者Juliano Silva
相关产品推荐
相关产品推荐

