如何在Next.js中判断用户是否可执行前进导航?
判断前进导航可行性的实现方案
在Next.js场景下,由于navigation.canGoForward属于实验性API,兼容性不足,我们可以通过手动维护客户端路由的历史栈状态来精准判断是否允许前进操作,具体实现如下:
核心实现代码
import { BackIcon, ForwardIcon } from "@sfe/utils/icons"; import { StyledButtonsWrapper, StyledRouteButton } from "./style"; import { useRouter, usePathname } from "next/navigation"; import { useState, useEffect } from "react"; const RouteButtons = () => { const router = useRouter(); const pathname = usePathname(); // 维护后退历史栈与前进历史栈 const [backStack, setBackStack] = useState<string[]>([]); const [forwardStack, setForwardStack] = useState<string[]>([]); useEffect(() => { // 处理路由变化,区分正常跳转与后退操作 const handleRouteChange = (url: string) => { const targetPath = new URL(url).pathname; // 正常跳转(非后退):更新后退栈,清空前进栈 if (backStack.at(-1) !== targetPath) { setBackStack(prev => [...prev, pathname]); setForwardStack([]); } else { // 后退操作:将当前路径移至前进栈,更新后退栈 setBackStack(prev => prev.slice(0, -1)); setForwardStack(prev => [...prev, pathname]); } }; // 重写forward方法,同步维护前进栈状态 const originalForward = router.forward; router.forward = () => { if (forwardStack.length > 0) { setForwardStack(prev => prev.slice(0, -1)); setBackStack(prev => [...prev, pathname]); } originalForward(); }; // 绑定路由变化监听 const unsubRouteChange = router.events.on("routeChangeComplete", handleRouteChange); // 组件卸载时清理监听与恢复原方法 return () => { unsubRouteChange(); router.forward = originalForward; }; }, [router, pathname, backStack, forwardStack]); // 判断可前进/后退状态 const canGoForward = forwardStack.length > 0; const canGoBack = backStack.length > 0; return ( <StyledButtonsWrapper> <StyledRouteButton onClick={() => router.back()} $disabled={!canGoBack} > <BackIcon /> </StyledRouteButton> <StyledRouteButton onClick={() => router.forward()} $disabled={!canGoForward} > <ForwardIcon /> </StyledRouteButton> </StyledButtonsWrapper> ); }; export default RouteButtons;
方案说明
- 状态维护:用
backStack记录可后退的路径历史,forwardStack记录可前进的路径历史。 - 路由事件监听:通过
routeChangeComplete事件区分正常跳转和后退操作,同步更新两个栈的状态:- 正常跳转时,将当前路径加入后退栈,并清空前进栈(因为此时无法再前进到之前的路径)。
- 后退操作时,将当前路径移至前进栈,同时从后退栈移除顶部元素。
- 重写forward方法:在执行前进操作时,同步更新前进栈和后退栈的状态,确保状态与实际路由一致。
- 状态判断:通过
forwardStack.length > 0判断是否可以前进,backStack.length > 0判断是否可以后退(比原window.history.length更准确,避免Next.js客户端路由与浏览器history不同步的问题)。
备选降级方案(兼容性优先)
如果可以接受实验性API的兼容性风险,也可以采用检测API可用性的方式降级处理:
import { useState, useEffect } from "react"; import { BackIcon, ForwardIcon } from "@sfe/utils/icons"; import { StyledButtonsWrapper, StyledRouteButton } from "./style"; import { useRouter } from "next/navigation"; const RouteButtons = () => { const router = useRouter(); const [canGoForward, setCanGoForward] = useState(false); useEffect(() => { // 检测navigation.canGoForward是否可用 const checkForwardCapability = async () => { if (typeof navigation !== "undefined" && typeof navigation.canGoForward === "function") { const result = await navigation.canGoForward(); setCanGoForward(result); } }; checkForwardCapability(); // 路由变化时重新检测 const unsub = router.events.on("routeChangeComplete", checkForwardCapability); return () => unsub(); }, [router]); return ( <StyledButtonsWrapper> <StyledRouteButton onClick={() => router.back()} $disabled={!window?.history?.length} > <BackIcon /> </StyledRouteButton> <StyledRouteButton onClick={() => router.forward()} $disabled={!canGoForward} > <ForwardIcon /> </StyledRouteButton> </StyledButtonsWrapper> ); };
内容的提问来源于stack exchange,提问作者Ali Navidi
相关产品推荐
相关产品推荐

