You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于React Router v4实现响应式Master/Detail布局?

解答你的React Router响应式布局疑问&修正代码

先逐个回答你的6个疑问:

  • 1) AppLayout组件是什么?
    这是一个自定义的全局布局组件,不是React Router自带的。它通常用来包裹整个应用的通用结构,比如顶部导航栏、页脚、全局样式容器等。示例里只是用它做占位,你完全可以像代码里那样注释掉,或者自己实现一个适合你项目的版本。

  • 2) 代码中引用的Layout组件是什么,是否重要?
    和AppLayout类似,这是Invoices页面专属的自定义布局组件,用来控制导航栏和内容区的排版(比如大屏时左右排列,小屏时上下堆叠)。它不是必须的,但如果需要统一页面内的布局样式,会很有用。你用<div>代替完全没问题,只是要自己调整布局样式。

  • 3) Media标签是否指react-media库?
    对的!就是react-media这个第三方库的组件,用来在React组件中处理媒体查询。你代码里已经正确导入了,不过要确保已经通过npm install react-media或者yarn add react-media安装了它。

  • 4) Media query={PRETTY_SMALL}是否等价于Media query={{ maxWidth: 599 }}?
    完全等价!示例里的PRETTY_SMALL就是一个提前定义好的常量,内容应该就是{ maxWidth: 599 },你直接写这个对象是没问题的。

  • 5) 示例中没有Router组件,但我认为这是必需的?
    你说得太对了!React Router v4+必须用BrowserRouter(或者HashRouter)包裹所有路由相关的组件,这是示例省略的关键部分——它默认你已经在应用的最外层添加了Router。你的代码里漏掉了这一步,这是路由不生效的核心原因之一。

  • 6) 示例中没有Link对象?
    示例只是简化了结构,实际使用中必须添加Link组件来实现路由跳转。你需要在InvoicesNav里添加Link,才能跳转到仪表盘和具体的发票页面,测试响应式布局的效果。


修正后的完整代码

下面是基于你的代码修改后的可运行版本,我添加了Router包裹、导航链接,还有基础的布局样式来实现Master/Detail的响应式效果:

import Media from "react-media";
import React from 'react';
import { Route, Switch, Redirect, BrowserRouter as Router, Link } from 'react-router-dom';

// 模拟发票数据
const data = [
  { id: 1, to: 'Mr. Smith', amount: 10 },
  { id: 2, to: 'Mrs. Jones', amount: 100 }
];

// 导航组件:添加跳转链接
const InvoicesNav = () => (
  <div style={{ padding: '1rem', backgroundColor: '#f5f5f5' }}>
    <h3>发票导航</h3>
    <ul style={{ listStyle: 'none', padding: 0 }}>
      <li style={{ margin: '0.5rem 0' }}>
        <Link to="/invoices/dashboard">仪表盘</Link>
      </li>
      {data.map(invoice => (
        <li key={invoice.id} style={{ margin: '0.5rem 0' }}>
          <Link to={`/invoices/${invoice.id}`}>发票 #{invoice.id}</Link>
        </li>
      ))}
    </ul>
  </div>
);

const Dashboard = () => (
  <div style={{ padding: '1rem' }}>
    <h2>仪表盘</h2>
    <p>这里显示发票统计信息</p>
  </div>
);

// 接收路由参数,显示具体发票详情
const Invoice = ({ match }) => {
  const invoice = data.find(item => item.id === parseInt(match.params.id));
  return (
    <div style={{ padding: '1rem' }}>
      <h2>发票详情 #{invoice.id}</h2>
      <p>收件人: {invoice.to}</p>
      <p>金额: ${invoice.amount}</p>
    </div>
  );
};

// 自定义布局组件(可选,用来控制导航和内容的排列)
const Layout = ({ children }) => (
  <div style={{ display: 'flex', flexWrap: 'wrap' }}>
    {children}
  </div>
);

const Invoices = () => (
  <Layout>
    {/* 始终显示导航 */}
    <InvoicesNav />
    <Media query={{ maxWidth: 599 }}>
      {screenIsSmall => screenIsSmall ? (
        // 小屏:导航和内容堆叠,访问/invoices时不重定向(需要用户主动点击链接)
        <Switch>
          <Route exact path="/invoices/dashboard" component={Dashboard} />
          <Route path="/invoices/:id" component={Invoice} />
        </Switch>
      ) : (
        // 大屏:导航和内容并排,访问/invoices时自动重定向到仪表盘
        <Switch>
          <Route exact path="/invoices/dashboard" component={Dashboard} />
          <Route path="/invoices/:id" component={Invoice} />
          <Redirect from="/invoices" to="/invoices/dashboard" />
        </Switch>
      )}
    </Media>
  </Layout>
);

const App = () => (
  <Router>
    <div style={{ maxWidth: '1200px', margin: '0 auto' }}>
      <h1>发票管理系统</h1>
      <Route path="/invoices" component={Invoices} />
      {/* 可以添加根路由重定向 */}
      <Redirect exact from="/" to="/invoices" />
    </div>
  </Router>
);

export default App;

代码说明:

  1. 添加了Router包裹:用BrowserRouter包裹了整个App,这是路由生效的前提。
  2. 完善了InvoicesNav:添加了Link组件,实现跳转到仪表盘和具体发票页面。
  3. 添加了布局样式:用Layout组件实现了大屏时导航和内容并排,小屏时堆叠的效果。
  4. Invoice组件接收路由参数:通过match.params.id获取发票ID,显示对应详情。
  5. 根路由重定向:访问根路径/时自动跳转到/invoices,方便测试。

现在你运行代码后,调整浏览器窗口大小就能看到响应式效果:

  • 小屏(≤599px):导航和内容上下堆叠,访问/invoices时不会自动跳转,需要点击导航链接。
  • 大屏(>599px):导航在左侧,内容在右侧,访问/invoices时自动跳转到仪表盘。

内容的提问来源于stack exchange,提问作者Phil Giggles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:15:00