如何用preact-router实现路由保护?地址栏访问受保护路由失效问题
Preact-Router 路由保护问题解决方案
问题描述
在组件层面结合AuthContext实现路由保护时,应用内导航访问受保护路由(如/cart)功能正常,但通过浏览器地址栏直接访问该路由时,跳转至/login的逻辑未触发,仅显示空白页面。
相关代码如下:
CartPage 组件
export const CartPage = (): JSX.Element => { const { isAuthenticated } = useAuthContext(); return ( <> {isAuthenticated ? ( <Cart /> ) : ( (() => { route("/login"); })() )} </> ); };
App 组件
export function App() { return ( <div> <AuthProvider> <Navbar /> <Layout> <ViewToggleProvider> <Router> <Route path="/" component={HomePage} /> <AsyncRoute path="/login" component={LoginPage} /> <AsyncRoute exact path="/cart" component={CartPage} /> <AsyncRoute path="/orders/:orderId" component={OrderDetailsPage} /> </Router> </ViewToggleProvider> </Layout> </AuthProvider> </div> ); }
原因分析
直接通过地址栏访问时,组件渲染阶段isAuthenticated可能还处于初始状态(比如用户认证信息是异步获取的),此时立即执行route()跳转,preact-router的路由初始化流程尚未完成,导致跳转逻辑失效。同时,组件返回空的<></>,所以页面显示空白。另外,在JSX渲染中直接执行立即执行函数属于副作用操作,违反Preact渲染纯函数原则,易引发不可预期行为。
解决方案
方案1:修复组件层面的路由保护逻辑
改用useEffect在组件挂载后执行跳转逻辑,确保路由环境已准备就绪,同时监听isAuthenticated的变化:
import { useEffect } from 'preact/hooks'; import { useNavigate } from 'preact/router'; // 推荐使用useNavigate替代直接调用route() export const CartPage = (): JSX.Element => { const { isAuthenticated } = useAuthContext(); const navigate = useNavigate(); useEffect(() => { // 未认证时跳转至登录页 if (!isAuthenticated) { navigate('/login'); } }, [isAuthenticated, navigate]); // 未认证时返回加载状态,避免空白页面 if (!isAuthenticated) { return <div>加载中...</div>; } return <Cart />; };
方案2:路由层面统一实现保护(更推荐)
创建通用的ProtectedRoute组件,集中管理所有受保护路由,避免重复逻辑:
1. 实现ProtectedRoute组件
import { useEffect } from 'preact/hooks'; import { useNavigate } from 'preact/router'; export const ProtectedRoute = ({ children }) => { const { isAuthenticated } = useAuthContext(); const navigate = useNavigate(); useEffect(() => { if (!isAuthenticated) { navigate('/login'); } }, [isAuthenticated, navigate]); if (!isAuthenticated) { return <div>加载中...</div>; // 可替换为自定义加载组件 } return children; };
2. 在App中使用ProtectedRoute
export function App() { return ( <div> <AuthProvider> <Navbar /> <Layout> <ViewToggleProvider> <Router> <Route path="/" component={HomePage} /> <AsyncRoute path="/login" component={LoginPage} /> {/* 保护购物车路由 */} <Route exact path="/cart"> <ProtectedRoute> <CartPage /> </ProtectedRoute> </Route> {/* 保护订单详情路由 */} <Route path="/orders/:orderId"> <ProtectedRoute> <OrderDetailsPage /> </ProtectedRoute> </Route> </Router> </ViewToggleProvider> </Layout> </AuthProvider> </div> ); }
若需结合AsyncRoute使用,可调整为:
<AsyncRoute exact path="/cart" component={() => ( <ProtectedRoute> <CartPage /> </ProtectedRoute> )} />
方案对比
- 组件层面修复:适合单个路由的简单保护,但逻辑分散,维护成本高。
- 路由层面实现:集中管理所有受保护路由,逻辑统一,便于后续扩展(如添加权限校验、统一加载状态处理等),是更优的实践方案。
内容的提问来源于stack exchange,提问作者octavemirbeau
相关产品推荐
相关产品推荐

