React学习教程,从入门到精通,React Router 语法知识点及使用方法详解(28)

React Router 语法知识点及使用方法详解


📌 当前环境说明

本文基于 React Router v5 编写(因你提供的知识点如 withRouterSwitchPrompt 等属于 v5 特性)。

若使用 React Router v6 ,部分 API 已废弃或重构(如 Switch → Routes, withRouter → hooks, Prompt → unstable_usePrompt 等)。


一、React Router 基础路由配置

✅ 1. 安装 React Router

bash 复制代码
npm install react-router-dom@5

✅ 2. 简单示例:网站列表(基础路由)

🎯 功能说明:

  • 首页 / 显示网站列表
  • 点击跳转到对应网站详情 /site/:id
  • 使用 BrowserRouter, Route, Link, Switch

🧩 代码示例:

jsx 复制代码
// App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';

// 模拟数据
const sites = [
  { id: 1, name: 'Google', url: 'https://google.com' },
  { id: 2, name: 'GitHub', url: 'https://github.com' },
  { id: 3, name: 'React', url: 'https://reactjs.org' },
];

// 首页组件:显示网站列表
function Home() {
  return (
    <div>
      <h2>🌐 网站列表</h2>
      <ul>
        {sites.map(site => (
          <li key={site.id}>
            {/* 使用 Link 进行客户端导航,不刷新页面 */}
            <Link to={`/site/${site.id}`}>{site.name}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

// 详情页组件:根据 URL 参数显示网站信息
function SiteDetail({ match }) {
  // match.params.id 获取动态路由参数
  const site = sites.find(s => s.id === parseInt(match.params.id));
  if (!site) return <h3>网站不存在</h3>;

  return (
    <div>
      <h2>📌 {site.name} 详情页</h2>
      <p>网址:<a href={site.url} target="_blank" rel="noopener noreferrer">{site.url}</a></p>
      <Link to="/">⬅ 返回首页</Link>
    </div>
  );
}

// 主应用组件
function App() {
  return (
    <Router>
      <div style={{ padding: '20px' }}>
        <h1>🔗 React Router 网站导航系统</h1>
        <hr />
        {/* Switch 确保只匹配第一个符合条件的路由 */}
        <Switch>
          {/* 精确匹配首页 */}
          <Route exact path="/" component={Home} />
          {/* 动态路由匹配详情页 */}
          <Route path="/site/:id" component={SiteDetail} />
          {/* 404 页面 */}
          <Route>
            <h3>⛔ 页面未找到</h3>
            <Link to="/">返回首页</Link>
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

export default App;

二、路由组件的属性(props)

当组件通过 Route 渲染时,会自动注入以下 props:

  • history:用于编程式导航(如 history.push()
  • location:当前 URL 信息(如 pathname, search, state
  • match:匹配信息(如 params, path, url

🧩 示例:打印路由属性

jsx 复制代码
function DebugRoute({ history, location, match }) {
  return (
    <div>
      <h3>🔍 路由调试信息</h3>
      <pre>
        {JSON.stringify({ history: 'object', location, match }, null, 2)}
      </pre>
    </div>
  );
}

// 在路由中使用
<Route path="/debug" component={DebugRoute} />

三、Switch 组件

Switch 只渲染第一个匹配的 RouteRedirect,避免多个路由同时激活。

🧩 示例:不使用 Switch 的问题

jsx 复制代码
{/* ❌ 不使用 Switch ------ 可能多个组件同时渲染 */}
<Route path="/" component={Home} />
<Route path="/about" component={About} />
{/* 当访问 /about 时,两个都会匹配(因为 / 包含在 /about 中)*/}

✅ 正确用法:

jsx 复制代码
<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/about" component={About} />
  <Route path="/contact" component={Contact} />
  {/* 默认 fallback */}
  <Route component={NotFound} />
</Switch>

四、路由匹配 matchPath()

用于在组件外或逻辑中手动匹配路径(如在 Redux 中判断当前路由)。

🧩 语法:

js 复制代码
import { matchPath } from 'react-router-dom';

const match = matchPath("/users/123", {
  path: "/users/:id",
  exact: true,
  strict: false
});

🧩 示例:在组件外判断路由

jsx 复制代码
import { matchPath } from 'react-router-dom';

function App() {
  const currentPath = window.location.pathname;

  const userMatch = matchPath(currentPath, {
    path: "/user/:id",
    exact: true
  });

  if (userMatch) {
    console.log("当前是用户页,ID:", userMatch.params.id);
  }

  return (
    <Router>
      {/* ... */}
    </Router>
  );
}

五、静态路由 vs 动态路由

✅ 静态路由

路径固定,无参数。

jsx 复制代码
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />

✅ 动态路由

路径含参数,如 :id, :name

jsx 复制代码
<Route path="/user/:id" component={UserProfile} />
<Route path="/category/:categoryName" component={CategoryPage} />

🧩 示例:动态路由 + 参数获取

jsx 复制代码
function UserProfile({ match }) {
  const { id } = match.params;
  return <h2>用户ID:{id}</h2>;
}

// 支持多个参数
<Route path="/post/:year/:month/:slug" component={BlogPost} />

function BlogPost({ match }) {
  const { year, month, slug } = match.params;
  return (
    <div>
      <h2>文章:{slug}</h2>
      <p>发布于 {year} 年 {month} 月</p>
    </div>
  );
}

六、各种路由器(Routers)

✅ 1. BrowserRouter(最常用)

使用 HTML5 History API(pushState, replaceState),URL 看起来干净:http://example.com/about

jsx 复制代码
import { BrowserRouter } from 'react-router-dom';

<BrowserRouter>
  <App />
</BrowserRouter>

✅ 2. HashRouter

使用 URL hash(#),兼容老浏览器,URL 如:http://example.com/#/about

jsx 复制代码
import { HashRouter } from 'react-router-dom';

<HashRouter>
  <App />
</HashRouter>

✅ 3. MemoryRouter

用于测试或非浏览器环境(如 React Native 之外的场景),URL 不反映在地址栏。

jsx 复制代码
import { MemoryRouter } from 'react-router-dom';

<MemoryRouter initialEntries={['/home', '/about']} initialIndex={0}>
  <App />
</MemoryRouter>

✅ 4. NativeRouter(React Native 专用)

用于 React Native 应用,不在 Web 环境使用。

jsx 复制代码
// 仅在 React Native 中
import { NativeRouter } from 'react-router-native';

✅ 5. StaticRouter(服务端渲染 SSR)

用于 Node.js 服务端渲染,需传入 locationcontext

jsx 复制代码
// server.js (Express 示例)
import { StaticRouter } from 'react-router-dom';

app.get('*', (req, res) => {
  const context = {};
  const markup = ReactDOMServer.renderToString(
    <StaticRouter location={req.url} context={context}>
      <App />
    </StaticRouter>
  );

  if (context.url) {
    res.redirect(301, context.url);
  } else {
    res.send(`
      <!DOCTYPE html>
      <html>
        <body>
          <div id="root">${markup}</div>
          <script src="/client.js"></script>
        </body>
      </html>
    `);
  }
});

七、React Router 特性组件

✅ 1. Prompt 组件 ------ 离开页面确认

在用户离开当前页面前弹出确认框(如表单未保存)。

jsx 复制代码
import { Prompt } from 'react-router-dom';

function ContactForm() {
  const [isBlocking, setIsBlocking] = useState(false);

  return (
    <div>
      {/* 当 isBlocking 为 true 时,离开页面会弹出提示 */}
      <Prompt
        when={isBlocking}
        message="你有未保存的内容,确定要离开吗?"
      />

      <textarea
        onChange={(e) => setIsBlocking(e.target.value.length > 0)}
        placeholder="输入内容..."
      />
      <button onClick={() => setIsBlocking(false)}>提交</button>
    </div>
  );
}

⚠️ 注意:现代浏览器对 Prompt 限制较多,部分浏览器可能忽略自定义消息。


✅ 2. withRouter 高阶组件(HOC)

history, location, match 注入到非路由组件中。

jsx 复制代码
import { withRouter } from 'react-router-dom';

function BackButton({ history }) {
  return (
    <button onClick={() => history.goBack()}>
      ⬅ 返回上一页
    </button>
  );
}

// 包装后即可获得路由属性
export default withRouter(BackButton);

在类组件中使用:

jsx 复制代码
class UserProfile extends React.Component {
  handleEdit = () => {
    this.props.history.push(`/user/${this.props.match.params.id}/edit`);
  };

  render() {
    return (
      <div>
        <h2>用户页</h2>
        <button onClick={this.handleEdit}>编辑</button>
      </div>
    );
  }
}

export default withRouter(UserProfile);

💡 v6 替代方案:使用 useNavigate, useLocation, useParams hooks。


✅ 3. Redirect 组件 ------ 重定向

用于登录跳转、页面迁移、权限控制等。

基本用法:

jsx 复制代码
<Redirect to="/login" />

带状态跳转:

jsx 复制代码
<Redirect
  to={{
    pathname: "/login",
    state: { from: props.location } // 传递来源页,登录后可跳回
  }}
/>

条件重定向示例:

jsx 复制代码
function PrivateRoute({ component: Component, isAuthenticated, ...rest }) {
  return (
    <Route
      {...rest}
      render={props =>
        isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Redirect to={{ pathname: "/login", state: { from: props.location } }} />
        )
      }
    />
  );
}

// 使用
<PrivateRoute path="/dashboard" component={Dashboard} isAuthenticated={userLoggedIn} />

八、综合性案例:带权限控制的后台管理系统

jsx 复制代码
// App.js - 综合案例
import React, { useState } from 'react';
import {
  BrowserRouter as Router,
  Route,
  Link,
  Switch,
  Redirect,
  Prompt,
  withRouter
} from 'react-router-dom';

// 模拟登录状态
const fakeAuth = {
  isAuthenticated: false,
  authenticate(cb) {
    fakeAuth.isAuthenticated = true;
    setTimeout(cb, 100); // 模拟异步
  },
  signout(cb) {
    fakeAuth.isAuthenticated = false;
    setTimeout(cb, 100);
  }
};

// 登录页
function Login({ history }) {
  const [redirectToReferrer, setRedirectToReferrer] = useState(false);

  const login = () => {
    fakeAuth.authenticate(() => {
      setRedirectToReferrer(true);
    });
  };

  if (redirectToReferrer) {
    return <Redirect to="/dashboard" />;
  }

  return (
    <div>
      <h2>🔒 登录</h2>
      <button onClick={login}>登录系统</button>
    </div>
  );
}

// 仪表盘(需登录)
function Dashboard() {
  const [formChanged, setFormChanged] = useState(false);

  return (
    <div>
      <h2>📊 仪表盘</h2>
      <Prompt
        when={formChanged}
        message="有未保存的更改,确定离开?"
      />
      <p>模拟表单:</p>
      <input type="text" onChange={() => setFormChanged(true)} />
      <button onClick={() => setFormChanged(false)}>保存</button>
    </div>
  );
}

// 设置页
function Settings() {
  return <h2>⚙️ 设置页面</h2>;
}

// 导航栏(使用 withRouter 获取 history)
const Navigation = withRouter(({ history, location }) => (
  <nav>
    <ul style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}>
      <li><Link to="/dashboard">仪表盘</Link></li>
      <li><Link to="/settings">设置</Link></li>
      <li>
        {fakeAuth.isAuthenticated ? (
          <button onClick={() => {
            fakeAuth.signout(() => history.push('/'));
          }}>退出登录</button>
        ) : (
          <Link to="/login">登录</Link>
        )}
      </li>
    </ul>
    <p>📍 当前路径:{location.pathname}</p>
  </nav>
));

// 私有路由组件
function PrivateRoute({ component: Component, ...rest }) {
  return (
    <Route
      {...rest}
      render={props =>
        fakeAuth.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{
              pathname: "/login",
              state: { from: props.location }
            }}
          />
        )
      }
    />
  );
}

// 主应用
function App() {
  return (
    <Router>
      <div style={{ padding: '20px' }}>
        <h1>🔐 后台管理系统</h1>
        <Navigation />

        <Switch>
          <Route exact path="/" render={() => <h2>欢迎!请登录进入系统</h2>} />
          <Route path="/login" component={Login} />
          <PrivateRoute path="/dashboard" component={Dashboard} />
          <PrivateRoute path="/settings" component={Settings} />
          <Route render={() => <h3>⛔ 404 页面不存在</h3>} />
        </Switch>
      </div>
    </Router>
  );
}

export default App;

九、本章小结

知识点 作用说明
BrowserRouter 使用 HTML5 History API,推荐用于现代 Web 应用
Route 定义路径与组件映射关系
Link 声明式导航,替代 <a> 标签
Switch 只渲染第一个匹配路由,避免冲突
match.params 获取动态路由参数(如 /user/:id
Redirect 重定向到其他路由,常用于登录跳转
Prompt 离开页面前确认,防止数据丢失
withRouter 非路由组件获取路由属性(v5),v6 用 hooks 替代
matchPath 手动匹配路径,适用于逻辑判断
HashRouter 兼容老浏览器,URL 含 #
StaticRouter 服务端渲染专用

🚀 升级建议(React Router v6)

若你计划使用 v6,请注意:

  • SwitchRoutes
  • Route componentRoute element={<Component />}
  • withRouteruseNavigate(), useLocation(), useParams()
  • Promptunstable_usePrompt(实验性)或自定义逻辑
  • exact 属性默认启用

✅ 以上内容涵盖 React Router v5 核心知识点 + 详细案例 + 综合实战,可直接用于项目开发或学习参考。

相关推荐
小志开发3 小时前
SQL从入门到起飞:完整数据库操作练习
数据库·sql·学习·oracle·sqlserver·navicat
木觞清3 小时前
补环境-JS原型链检测:在Node.js中完美模拟浏览器原型环境
开发语言·javascript·node.js
我是华为OD~HR~栗栗呀3 小时前
华为od-前端面经-22届非科班
java·前端·c++·后端·python·华为od·华为
黄毛火烧雪下3 小时前
React中Class 组件 vs Hooks 对照
前端·javascript·react.js
潲爺4 小时前
Java IDEA学习之路:第二周课程笔记归纳
java·笔记·学习
gnip4 小时前
工作常用设计模式
前端·javascript
明明真系叻4 小时前
量子计算学习笔记(1)
笔记·学习·量子计算
码农阿日5 小时前
【日常学习-理解Langchain】从问题出发,我理解了LangChain为什么必须这么设计
服务器·学习·langchain
前端达人5 小时前
「React实战面试题」useEffect依赖数组的常见陷阱
前端·javascript·react.js·前端框架·ecmascript