Skip to content

路由守卫(导航守卫)

导航守卫(Navigation Guards)是 React Router 中用于在路由变化时执行特定逻辑的功能。它们可以用来验证用户权限、加载数据或阻止导航等。虽然 React Router 没有像 Vue Router 那样内置的全局导航守卫,但你可以通过几种方式实现类似的行为。

1. 使用高阶组件 (HOC) 或自定义 Hook

你可以在组件层级上使用 HOC 或自定义 Hook 来实现导航守卫逻辑。这通常涉及检查某些条件(如登录状态),并在满足条件时允许导航继续,否则重定向到其他页面或显示错误信息。

示例:自定义 Hook

jsx
import { useEffect } from 'react';
import { useHistory, useLocation } from 'react-router-dom';

function useRouteGuard(isAuthenticated) {
  const history = useHistory();
  const location = useLocation();

  useEffect(() => {
    if (!isAuthenticated) {
      // 如果未认证,则重定向到登录页,并保存当前路径以便后续跳转回来
      history.push({
        pathname: '/login',
        state: { from: location }
      });
    }
  }, [isAuthenticated, history, location]);
}

// 在受保护的组件中使用
function ProtectedComponent() {
  const isAuthenticated = /* 你的认证逻辑 */;
  useRouteGuard(isAuthenticated);

  return <div>Protected Content</div>;
}

2. 使用 Prompt 组件(v5)

React Router v5 提供了一个名为 <Prompt> 的组件,它可以阻止用户离开当前页面,直到他们确认是否真的要离开。这对于防止用户意外丢失未保存的数据非常有用。

示例:Prompt

jsx
import { Prompt } from "react-router-dom";

function FormWithPrompt({ isBlocking }) {
  return (
    <form>
      {/* 表单内容 */}
      <Prompt
        when={isBlocking}
        message="Are you sure you want to leave? You have unsaved changes."
      />
    </form>
  );
}

3. 自定义中间件(适用于更复杂的场景)

对于更复杂的需求,比如基于角色的访问控制或动态加载资源,你可以创建一个自定义中间件来拦截所有的路由变化。这可以通过监听 history.listen 事件来实现。

示例:全局导航守卫

jsx
import { createBrowserHistory } from "history";
import { useEffect } from "react";
import { useHistory } from "react-router-dom";

const customHistory = createBrowserHistory();

function useGlobalGuard() {
  const history = useHistory();

  useEffect(() => {
    const unlisten = customHistory.listen((location, action) => {
      // 在这里添加你的守卫逻辑
      console.log("The current URL is", location.pathname);
      console.log("It changed via", action); // PUSH, REPLACE, POP

      // 根据需要执行一些操作,例如:
      // - 检查用户是否已登录
      // - 加载必要的数据
      // - 显示加载指示器
      // - 等等...
    });

    return () => {
      unlisten();
    };
  }, [history]);
}

// 在应用顶层使用此 Hook
function App() {
  useGlobalGuard();
  // 其他应用代码...
}

4. 使用 useEffect 监听 location 变化(v6)

React Router v6 不再支持 <Prompt> 组件,但是你可以利用 useEffectuseLocation Hooks 来监听位置变化并实施相应的逻辑。

示例:监听位置变化

jsx
import { useEffect, useState } from 'react';
import { useLocation, Navigate } from 'react-router-dom';

function RouteGuard({ children, isAllowed }) {
  const [redirected, setRedirected] = useState(false);
  const location = useLocation();

  useEffect(() => {
    if (!isAllowed) {
      setRedirected(true);
    }
  }, [location, isAllowed]);

  if (redirected) {
    return <Navigate to="/unauthorized" />;
  }

  return children;
}

// 使用 RouteGuard 包装受保护的组件
function ProtectedPage() {
  const isAuthenticated = /* 你的认证逻辑 */;

  return (
    <RouteGuard isAllowed={isAuthenticated}>
      <h1>This is a protected page</h1>
    </RouteGuard>
  );
}

总结

虽然 React Router 没有直接提供类似于 Vue Router 的全局导航守卫 API,但你可以通过上述方法实现类似的功能。选择哪种方法取决于你的具体需求和项目的复杂度:

  • 自定义 Hook:适合于组件级别的导航守卫。
  • <Prompt>(仅限 v5):用于阻止用户离开表单或其他页面。
  • 自定义中间件:适用于需要对所有路由变化进行全局处理的情况。
  • 监听 location 变化(v6):一种灵活的方式来响应路由的变化。

Released under the MIT License.