本項目Github地址,歡迎star
目錄
這篇指南是是用來解釋React Router使用時的心智模型。我們將其稱為“動態路由”,這與你可能更熟悉的“靜態路由”完全不同。
靜態路由
如果你使用過Rails,Express,Ember,Angular等框架,那么你已經使用過了靜態路由。 在這些框架中,當你在進行任何渲染之前,會將路由聲明為應用初始化的一部分。React Router pre-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,你需要忘記這一切。
背景
坦率地說,我們React Router直到v2版本所采取的開發方向感到非常沮喪。 我們(Michael和Ryan)感覺現在采用的這種路由方式受限于API,我們正在重新實現React(生命周期等)的部分,并且它與React給我們創造UI的心智模型不匹配。當我們在研討會之前在一家酒店的走廊互相討論時。我們互相問道:“如果我們使用我們在之前討論的模式構建路由會是什么樣子?”開發只需要幾個小時,我們就有了一個驗證的概念,我們知道她是未來我們想要路由。 我們最終意識到API并不應該在React的“外部”,這是一個由React的其余部分組成并自然落實到位的API。我們認為你會喜歡她。
動態路由
動態路由指的是在應用渲染時發生的路由,而不是在正在運行的應用之外配置或規定好的路由。這意味著幾乎所有東西都是React Router中的一個組件。接下來看一下這個API是如何工作的:
首先,創建一個路由組件,將其渲染在應用的頂部。
// 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
);
接下來,創建一個鏈接組件將其鏈接到一個新的地址。
const App = () => (
<div>
<nav>
<Link to="/dashboard">Dashboard</Link>
</nav>
</div>
);
最后,當用戶查看‘/dashboard'路徑時,渲染該路由并展示對應UI
const App = () => (
<div>
<nav>
<Link to="/dashboard">Dashboard</Link>
</nav>
<div>
<Route path="/dashboard" component={Dashboard} />
</div>
</div>
);
Route將渲染<Dashboard {... props} />,props是一些看起來像{match,location,history}這樣的特定的路由的屬性。如果用戶不在‘/dashboard'路徑下,那么Route將不渲染任何東西。這就是它的全部內容。
嵌套路由
很多路由庫都有一些“嵌套路由”的概念。如果你使用過React Router V4以前的版本,那么你就會知道我們也是這樣做的。然而當你從靜態路由配置移動到動態渲染路由時,該如何“嵌套路由”呢?
const App = () => (
<BrowserRouter>
{/* here's a div */}
<div>
{/* here's a Route */}
<Route path="/tacos" component={Tacos} />
</div>
</BrowserRouter>
);
// when the url matches `/tacos` this component renders
const Tacos = ({ match }) => (
// here's a nested div
<div>
{/* here's a nested Route,
match.url helps us make a relative path */}
<Route path={match.url + "/carnitas"} component={Carnitas} />
</div>
);
如上代碼,React Router并沒有嵌套的API,Router僅僅是一個組件,就想一個普通的div一樣。
響應路由
設想一下用戶導航到'/invoice'。你的應用需要適用于不同的尺寸的屏幕,當她們具有較窄的視口時,你只需向其顯示發票列表和到發票詳情的鏈接。她們可以導航到更深入的層級。
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 | |
| | |
+----------------------+---------------------------+
現在暫停考慮一下兩種屏幕尺寸的/invoice URL,她是大尺寸屏幕的有效路由嗎?我們應該把右邊的東西放在什么位置?
Large Screen
url: /invoices
+----------------------+---------------------------+
| | |
| Dashboard | |
| | |
+----------------------+ |
| | |
| Invoice 01 | |
| | |
+----------------------+ |
| | |
| Invoice 02 | ??? |
| | |
+----------------------+ |
| | |
| Invoice 03 | |
| | |
+----------------------+ |
| | |
| Invoice 04 | |
| | |
+----------------------+---------------------------+
在大屏幕上,'/invoice'不是有效的路線,但在小屏幕上她是有效的,更有趣的是,請考慮一個擁尺寸手機的人。她/他可以用橫屏和豎屏兩種方式來查看手機頁面。突然間,我們有足夠的空間來顯示類別-詳情這種UI,所以你應該立即重定向!
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的聲明可組合性來解決問題,因為幾乎每個“React Router問題”都可能是“React問題”。