前言
注意:因系列專注討論 History API,並非完整看完 Reacr-Router Source Code,如有錯誤歡迎隨時留言跟我反應
History API 系列講了那麼多終於回到當初話題中 React-router 中的 Route 跟網址變換是怎麼實作出來的,其實看了前兩篇介紹 Anchor 及 History API,應該已經蠻能延伸理解 React-Router 的運作原理,本篇只是真正的揭開面紗,以下會先簡介 React-Router 基本的使用 Router
、Route
、Switch
、Link
,及背後是如何達成網頁及頁面的切換。
React Router 範例
直接取官網的 Basic Example 來回顧一下
function BasicExample() {
return (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/dashboard">Dashboard</Link></li>
</ul>
<hr />
<Switch>
<Route exact path="/"><Home /></Route>
<Route path="/about"><About /></Route>
<Route path="/dashboard"><Dashboard /></Route>
</Switch>
</div>
</Router>
);
}
- 每次渲染時
Router
會依照當前 Router 路徑將Route
內有寫的路徑 match 一輪,再將 match 到的Route
內容交給Switch
去 render。 - 一進入到頁面時,
Router
會使用網址上的路徑初始化一次,並執行對應渲染。 - 點擊上方
Link
元件,會觸發將Router
切換路徑為Link
prop 中to
字串,進而重新 renderSwitch
。 - 當點擊上頁、下頁跳轉時,網址切換會觸發
Router
路徑切換,進而重新 renderSwitch
。
閱讀 Source Code
範例選用 BrowserRouter
為 Router
(原始碼連結)
一開頭註解就跟我們說了 React-Router 是使用 HTML5 History API 實作的 XD
但這邊使用的是一個再被包裝過的 History 套件,晚點來研究看看。
import React from "react";
import { Router } from "react-router";
import { createBrowserHistory as createHistory } from "history";
/**
* The public API for a <Router> that uses HTML5 history.
*/
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />;
}
}
Router
會使用 History 物件中的 location 網址路徑來初始化 Router 路徑(原始碼連結)
/**
* The public API for putting history on context.
*/
class Router extends React.Component {
this.state = {
location: props.history.location
};
...以下省略70行
}
Link
元件會 render 出 LinkAnchor
元件,點擊 LinkAnchor
時(原始碼連結)
預設會觸發 history
套件中的 push 或 replace method
沒錯,這兩個 method 也就是同 HTML5 history API 中的 pushState 及 replaceState。
const LinkAnchor = forwardRef(
let props = {
onClick= event => {
...省略
event.preventDefault();
navigate();
}
}
return <a {...props} />;
)
const Link = forwardRef(
(
component = LinkAnchor,
...省略
) => {
return (
<RouterContext.Consumer>
{context => {
const { history } = context;
navigate() {
const location = resolveToLocation(to,context.location);
const method = replace ? history.replace :history.push;
method(location);
}
...省略
return React.createElement(component, props);
}}
</RouterContext.Consumer>
);
}
);
Switch
使用路徑找尋當前 render 對象(原始碼)
class Switch extends React.Component {
render() {
return (
<RouterContext.Consumer>
{context => {
const location = this.props.location || context.location;
let element, match;
React.Children.forEach(this.props.children, child => {
... 以下省略
}}
</RouterContext.Consumer>
);
}
}
再來稍微看一下 History 套件的使用方法
- 結合了 location 物件,可以在套件中取得 history.location
- 簡化了 pushState、replaceState 原本的 title 欄位,注重路徑對應的 State
- 最大變化的事 onpopstate 事件也變成 history 內使用,可用 listen 及 unlisten 綁定及取消。
import { createBrowserHistory } from 'history'; const history = createBrowserHistory(); // Get the current location. const location = history.location; // Listen for changes to the current location. const unlisten = history.listen((location, action) => { // location is an object like window.location console.log(action, location.pathname, location.state); }); // Use push, replace, and go to navigate around. history.push('/home', { some: 'state' }); // To stop listening, call the function returned from listen(). unlisten();
小結
終於把 React-router 與 History API 關係這話題講完了,不知不覺也把七天文章寫完了,之後再來補寫一篇這七天寫作的心得吧,如果大家還有想了解哪些熱門套件背後的實作邏輯也歡迎在下方留言討論哦,先來去補眠了。