设计理念
本指南的目的是解释使用 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 应用要适应不同的屏幕尺寸。
- 对于视口狭窄的小屏幕,因只向他们显示发票清单和发票仪表板的链接。他们可以从那里更深入地导航。
Small Screen
url: /invoices
+----------------------+
| |
| Dashboard |
| |
+----------------------+
| |
| Invoice 01 |
| |
+----------------------+
| |
| Invoice 02 |
| |
+----------------------+
| |
| Invoice 03 |
| |
+----------------------+
| |
| Invoice 04 |
| |
+----------------------+
- 在较大的屏幕上,我们想显示一个主从视图,其中导航在左侧,仪表板或特定发票在右侧。
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 问题”。