设计理念

本指南的目的是解释使用 React Router 时要具有的思维模型。我们称之为“动态路由”,它与您可能更熟悉的“静态路由”完全不同。

静态路由

如果您使用过 Rails,Express,Ember,Angular 等,则使用了静态路由。在这些框架中,您将在进行任何渲染之前的应用程序初始化过程中声明路由。React Router < v4 也是静态的(大部分是静态的)。让我们看一下如何在 express 中配置路由:

// Express Style routing:
app.get('/', handleIndex)
app.get('/invoices', handleInvoices)
app.get('/invoices/:id', handleInvoice)
app.get('/invoices/:id/edit', handleInvoiceEdit)

app.listen()

请注意在应用监听之前如何声明路由。我们使用的客户端路由器是相似的。在 Angular 中,您先声明路由,然后 AppModule 在渲染之前将其导入顶层:

// Angular Style routing:
const appRoutes: Routes = [
  {
    path: 'crisis-center',
    component: CrisisListComponent
  },
  {
    path: 'hero/:id',
    component: HeroDetailComponent
  },
  {
    path: 'heroes',
    component: HeroListComponent,
    data: { title: 'Heroes List' }
  },
  {
    path: '',
    redirectTo: '/heroes',
    pathMatch: 'full'
  },
  {
    path: '**',
    component: PageNotFoundComponent
  }
]

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)]
})
export class AppModule {}

Ember 具有一个常规 routes.js 文件,该文件会为您读取并导入到应用程序中。同样,这是在您的应用渲染之前发生的。

// Ember Style Router:
Router.map(function() {
  this.route('about')
  this.route('contact')
  this.route('rentals', function() {
    this.route('show', { path: '/:rental_id' })
  })
})

export default Router

尽管 API 不同,但它们都共享“静态路由”模型。React Router 也跟进了直到 v4。为了成功使用 React Router,您需要忘记所有这些!:O

背景故事

坦率地说,React Router v2 中采取的方向感到非常沮丧。我们(Michael 和 Ryan)感到受到 API 的限制,认识到我们正在重新实现 React 的各个部分(生命周期等),而这与 React 为构建 UI 提供的思维模型不匹配。

我们正讨论该如何处理的工作的时候穿过酒店的走廊。我们互相问:“如果使用我们在讲习班中教学的模式建造路由器,那会是什么样?”

仅仅几个小时的开发时间,我们就获得了概念证明 PoC,我们知道这是我们希望的 react 路由的未来。我们最终得到的 API 并不是 React 之外的“ API”,它是由 React 的其余部分组成或自然地融入其中的。我们认为您会喜欢它的!

动态路由

当我们说动态路由,是指在您的应用渲染时发生的路由跳转,而不是在运行的应用之外的配置或约定中进行。这意味着几乎所有内容都是 React Router 中的一个组件。这是对该 API 的 60 秒回顾,以了解其工作原理:

首先,为你的开发环境选择一个 Router 组件,并在 react 应用的顶层渲染:

// react-native
import { NativeRouter } from 'react-router-native'

// react-dom (what we'll use here)
import { BrowserRouter } from 'react-router-dom'

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  el
)

接下来,获取<Link>组件以链接到新位置:

const App = () => (
  <div>
    <nav>
      <Link to="/dashboard">Dashboard</Link>
    </nav>
  </div>
)

最后,渲染一个 Route 以在用户访问时显示一些 UI /dashboard:

const App = () => (
  <div>
    <nav>
      <Link to="/dashboard">Dashboard</Link>
    </nav>
    <div>
      <Route path="/dashboard" component={Dashboard} />
    </div>
  </div>
)

该 Route 将使<Dashboard {...props}/>其中 props 包含一些 react-router 的具体属性,包括{ match, location, history }。如果用户没有在/dashboard 路径上, 那么 Route 将呈现 null。这几乎就是它的全部逻辑。

嵌套路由

许多路由器都有“嵌套路由”的概念。如果您使用过 v4 之前的 React Router 版本,您也会知道它也是如此!当您从静态路由配置转换为动态渲染的路由时,如何“嵌套路由”?好吧,您如何嵌套一个 div?

const App = () => (
  <BrowserRouter>
    <div>
      <Route path="/tacos" component={Tacos} />
    </div>
  </BrowserRouter>
)

// when the url matches `/tacos` this component renders
const Tacos = ({ match }) => (
  //这是一个嵌套路由
  <div>
    {/* 这是一个嵌套路由
        match.url 帮助我们定义相对路径 */}
    <Route path={match.url + '/carnitas'} component={Carnitas} />
  </div>
)

你会发现我们在没有嵌套 API 的情况下完成了嵌套路由的操作。Route 只是一个组件,就像 div。因此,要嵌套 Route 或 div,您只需…做就可以了。

让我们看看更加复杂的例子。

响应式路由

考虑用户导航到/invoices。您的 web 应用要适应不同的屏幕尺寸。

  1. 对于视口狭窄的小屏幕,因只向他们显示发票清单和发票仪表板的链接。他们可以从那里更深入地导航。
Small Screen
url: /invoices

+----------------------+
|                      |
|      Dashboard       |
|                      |
+----------------------+
|                      |
|      Invoice 01      |
|                      |
+----------------------+
|                      |
|      Invoice 02      |
|                      |
+----------------------+
|                      |
|      Invoice 03      |
|                      |
+----------------------+
|                      |
|      Invoice 04      |
|                      |
+----------------------+
  1. 在较大的屏幕上,我们想显示一个主从视图,其中导航在左侧,仪表板或特定发票在右侧。
Large Screen
url: /invoices/dashboard

+----------------------+---------------------------+
|                      |                           |
|      Dashboard       |                           |
|                      |   Unpaid:             5   |
+----------------------+                           |
|                      |   Balance:   $53,543.00   |
|      Invoice 01      |                           |
|                      |   Past Due:           2   |
+----------------------+                           |
|                      |                           |
|      Invoice 02      |                           |
|                      |   +-------------------+   |
+----------------------+   |                   |   |
|                      |   |  +    +     +     |   |
|      Invoice 03      |   |  | +  |     |     |   |
|                      |   |  | |  |  +  |  +  |   |
+----------------------+   |  | |  |  |  |  |  |   |
|                      |   +--+-+--+--+--+--+--+   |
|      Invoice 04      |                           |
|                      |                           |
+----------------------+---------------------------+

现在暂停一分钟,考虑/invoices 两种屏幕尺寸的网址。它甚至是大屏幕的有效路由吗?我们应该在右边放什么?

Large Screen
url: /invoices
+----------------------+---------------------------+
|                      |                           |
|      Dashboard       |                           |
|                      |                           |
+----------------------+                           |
|                      |                           |
|      Invoice 01      |                           |
|                      |                           |
+----------------------+                           |
|                      |                           |
|      Invoice 02      |             ???           |
|                      |                           |
+----------------------+                           |
|                      |                           |
|      Invoice 03      |                           |
|                      |                           |
+----------------------+                           |
|                      |                           |
|      Invoice 04      |                           |
|                      |                           |
+----------------------+---------------------------+

在大屏幕上,/invoices 不是有效路由,但在小屏幕上,则是有效路由!为了使事情变得更有趣,请考虑使用大型手机的人。他们可能/invoices 以纵向观看,然后将手机旋转为横向。突然,我们有足够的空间来显示主从界面,因此您应该立即进行重定向!React Router 先前版本的静态路由并没有真正解决这个问题的方法。但是,当路由是动态的时,您可以声明性地组合此功能。如果您开始考虑将路由选择为 UI,而不是静态配置,那么您的直觉将引导您进入以下代码

const App = () => (
  <AppLayout>
    <Route path="/invoices" component={Invoices} />
  </AppLayout>
)

const Invoices = () => (
  <Layout>
    {/* always show the nav */}
    <InvoicesNav />

    <Media query={PRETTY_SMALL}>
      {screenIsSmall =>
        screenIsSmall ? (
          // small screen has no redirect
          <Switch>
            <Route exact path="/invoices/dashboard" component={Dashboard} />
            <Route path="/invoices/:id" component={Invoice} />
          </Switch>
        ) : (
          // large screen does!
          <Switch>
            <Route exact path="/invoices/dashboard" component={Dashboard} />
            <Route path="/invoices/:id" component={Invoice} />
            <Redirect from="/invoices" to="/invoices/dashboard" />
          </Switch>
        )
      }
    </Media>
  </Layout>
)

当用户将手机从纵向旋转到横向时,此代码将自动将其重定向到仪表板。有效路由的集合根据用户手中的移动设备的动态性质而改变。这只是一个例子。我们可以讨论许多其他问题,但我们将总结以下建议:为了使您的直觉与 React Router 的直觉保持一致,请考虑组件而不是静态路由。考虑一下如何使用 React 的声明式可组合性解决问题,因为几乎每个“ React Router 问题”都可能是“ React 问题”。

最后修改时间: 50 seconds ago