博主翻译原教程目的(加了一些博主自己解读),一是博主自己进一步了解React Router 4,二是官网的教程确实还需加强引导性和增加案例多样性。从React Router 4 的心智模型。看后面对这个词的解释,就明白了有些伙伴们对4的质疑。
目的
此指南的目的为了说明使用 React Router 4 时的心智模型。我们称之为的”动态路由“,这与你熟悉的”静态路由“有很大的不同。
注释:心智模型是经由经验及学习,脑海中对某些事物发展的过程,所写下的剧本,是个体为了要了解和解释他们的经验,所建构的知识结构,该模型受限于个体关于他们经验的内隐理论,这可能有很多或很少的正确性。
静态路由
如果你使用 Rails, Express, Ember, Angular 等。你已经使用了静态路由。在这些框架中,在任何渲染发生之前,将申明路由作为应用程序初始化一部分。
React Router 4 之前的版本也是静态的(主要是)。让我们看看在 Express 里如何配置路由:
const express = require('express');
const app = express();
app.get('/', handleIndex)
app.get('/invoices', handleInvoices)
app.get('/invoices/:id', handleInvoice)
app.get('/invoices/:id/edit', handleInvoiceEdit)
app.listen()
注意:在应用程序监听之前如何让路由被申明。这和我们使用的客户端路由是相似的。在 Angular 中,首先申明路由并且在渲染之间把他们导入到顶级的 AppMoudule :
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 文件,它可以构建读取并导入到应用程序里。同样,在应用渲染之前发生。
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 4之前也是这样的模式。
如果要使用React Router,那么你需要忘记这一切!
背景故事
坦白的说,我们非常郁闷,因为第二版采用了 React Router。我们感到被API 限制了。认识到我们正在重新实现 React 的一部份(生命周期等),并且它不能符合 React 给我们编写的用户界面的心智模型。
一次研究讨论会前,我们正走走过一家酒店的长走廊。我们相互讨论:”如果我们使用在研讨论会中的模式构建路由器会是什么样?“
动态路由
静态路由:任何渲染发生之前,将申明路由作为应用程序初始化一部分。
当我们说动态路由,在应用程序正在渲染时发生的路由,不是运行应用程序之外的配置或约定中。在React Router 中一切都是一个组件。下面是一个 API 回顾,看下它如何工作的。
首先,在应用顶部
// react-native
import { NativeRouter } from 'react-router-native'
// react-dom
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
<BrowserRouter>
<App/>
</BrowserRouter>
), el)
其次,用 Link 组件去链接一个新定位:
const App = () => (
<div>
<nav>
<Link to="/dashboard">Dashboard</Link>
</nav>
</div>
)
最后,当用户访问 /dashboard
时,会去渲染一个路由去显示用户界面。
const App = () => (
<div>
<nav>
<Link to="/dashboard">Dashboard</Link>
</nav>
<div>
<Route path="/dashboard" component={Dashboard}/>
</div>
</div>
)
路由将渲染 <Dashboard {...props}/>,props
是一些路由器特定的东西,看起来像 {match, location, history}
。如果 /dashboard
链接不存在,那么路由将呈现为空。这几乎就是全部了。
嵌套路由
什么情况下用到嵌套路由,比如应用左侧边栏是个菜单,点击不同内容时,右侧界面对应相应一个路由并且随着变化。
许多路由有一些概念性的 ”嵌套路由"。如果你已经使用过 React Router 4 之前的版本,那么也将知道它是什么。当你从一个静态路由配置移到动态的渲染路由,知道怎么做吗?怎么嵌套一个div?
const App = () => (
<BrowserRouter>
{/* 这里有个div */}
<div>
{/* 路由 */}
<Route path="/tacos" component={Tacos}/>
</div>
</BrowserRouter>
)
// when the url matches `/tacos` this component renders
const Tacos = ({ match }) => (
// 有一个嵌套div
<div>
{/* 有一个嵌套路由,match.url 帮助我们做一个相对路径 */}
<Route
path={match.url + '/carnitas'}
component={Carnitas} />
</div>
)
可以看出,这里并没有嵌套 API ,路由只是一个组件而已,就像 div 一样。所以嵌套一个路由或 div , 照着这样做就行了。
响应式路由
用户先导航到 /invoices
。应用能自适应不同尺寸的屏幕,它们有一个小的适配器,而且能展示它们 invoices
列表和链接到 invoice/dashboard
。它们可以深度导航。
小屏幕
路由地址: /invoices
+----------------------+
| |
| Dashboard |
| |
+----------------------+
| |
| Invoice 01 |
| |
+----------------------+
| |
| Invoice 02 |
| |
+----------------------+
| |
| Invoice 03 |
| |
+----------------------+
| |
| Invoice 04 |
| |
+----------------------+
在一个大屏幕上,我们想点击左边的 Dashboard
就在右边显示一个对应导航的页面。
大屏幕
路由地址: /invoices/dashboard
+----------------------+---------------------------+
| | |
| Dashboard | |
| | Unpaid: 5 |
+----------------------+ |
| | Balance: $53,543.00 |
| Invoice 01 | |
| | Past Due: 2 |
+----------------------+ |
| | |
| Invoice 02 | |
| | +-------------------+ |
+----------------------+ | | |
| | | + + + | |
| Invoice 03 | | | + | | | |
| | | | | | + | + | |
+----------------------+ | | | | | | | | |
| | +--+-+--+--+--+--+--+ |
| Invoice 04 | |
| | |
+----------------------+---------------------------+
我们思考一下,/invoices
路由地址是适用于两种屏幕的,在大屏幕上,它是有效路由吗?它右边呈现什么内容了?
大屏幕
路由地址: /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>
{/* 一直显示的导航 */}
<InvoicesNav/>
<Media query={PRETTY_SMALL}>
{screenIsSmall => screenIsSmall
// 小屏幕时没有重定向
? <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
</Switch>
// 大屏幕时重定向!
: <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
// 重定向
<Redirect from="/invoices" to="/invoices/dashboard"/>
</Switch>
}
</Media>
</Layout>
)
隋着用户的手机从纵向旋转到横向,这段代码将自动重定向它们到 dashboard
,这一组有效路由改变取决于移动设备在用户手中的动态状态。
这仅是一个案例。还有很多其他可以讨论的,我们总结的建议:为了让你理解React Router ,想想它是组件,不是一个静态路由,思考如何用 React 声明性可组合性解决问题,因为几乎每个"React Router 问题"大概是个”React 问题“。