Create by jsliang on 2019-04-26 13:13:18
Recently revised in 2019-04-29 15:25:01
Hello 小夥伴們,如果覺得本文還不錯,記得給個 star , 小夥伴們的 star 是我持續更新的動力!GitHub 地址
一 目錄
不折騰的前端,和鹹魚有什麼區別
二 前言
前端路由,是指改變 URL 路徑的形式,從而切換到不同的頁面,例如:
localhost:3000/home
localhost:3000/user
通過切換不同的 URL,顯示不同的頁面,從而有了 路由 的概念。
這篇文章我們講解在 React 中如何通過 React Router 這個外掛,靈活使用路由。
jsliang 瞎吹的,最好自己百度 前端路由 是啥。
- 參考資料:
- React Router 官方文件
- React Router 中文文件
- React Router 中文文件(舊)
- React Router DOM 中文文件(一) - 簡書
- React Router DOM 中文文件(二) - 簡書
- 篇外話題:
網上有很多 React Router 文章了,例如:
為何 jsliang 要多次一舉?
- 你記錄你的,我記錄我的,互相不妨礙。
- 看這些跟看官網沒啥兩樣,所以我需要親自動手過一遍官網。
- 記錄我看官網的內容,順帶記錄我應用上去的例項,方便我下次回顧。
三 初試
當前版本:"react-router-dom": "^5.0.0"
首先,在 Create React App 中,我們引用 React Router:
npm i react-router-dom -S
然後,在 src 目錄下新建 pages 用來存放頁面,並修改 App.js:
案例:App.js
import React, { Fragment } from 'react';
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
import ScrollToTop from './components/ScrollToTop';
import Header from './components/Header';
import TimeLine from './pages/TimeLine';
import NotFound from './pages/404';
function App() {
return (
<Fragment>
<BrowserRouter>
<Header />
<ScrollToTop>
<Switch>
<Redirect from="/" to="/timeline" exact />
<Route path="/timeline" component={TimeLine}></Route>
<Route component={NotFound}></Route>
</Switch>
</ScrollToTop>
</BrowserRouter>
</Fragment>
);
}
export default App;
複製程式碼
最後,通過在 App.js 中如此定義,即可定義對應的元件,並渲染對應頁面和進行跳轉。
四 簡介
下面我們拿一些常用的進行介紹:
import {
BrowserRouter,
HashRouter,
Redirect,
Route,
NavLink,
Link,
MemoryRouter,
Switch,
withRouter
} from "react-router-dom";
複製程式碼
<BrowserRouter>
:路由元件包裹層。<Route>
和<Link>
的包裹層。<HashRouter>
:路由元件包裹層。相對於<BrowserRouter>
來說,更適合靜態檔案的伺服器。<Redirect>
:路由重定向。渲染<Redirect>
將使導航到一個新的地址。<Route>
:路由。定義一個路由頁面,用來匹配對應的元件(Component)和路由路徑。<NavLink>
:活躍連結。當 URL 中的路徑等於該路由定義的路徑時,該標籤可以呈現它定義的activeClassName
。<Link>
:連結。用來跳轉到<Route>
對應的路由(Component) 中。<MemoryRouter>
:暫未使用。<Router>
能在記憶體中儲存URL
的歷史記錄。很適合在測試環境和非瀏覽器環境中使用,例如 React Native。<Switch>
:路由分組。渲染與該地址匹配的第一個子節點<Route>
或者<Redirect>
。可以利用<Switch>
做分組。<withRouter>
:路由組合。通過<withRouter>
高階元件訪問history
物件的屬性和最近的<Route>
的match
。或者利用它來結合 Redux。
五 BrowserRouter
<BrowserRouter>
會為你建立一個專門的 history 物件,用來記錄你的路由,從而能夠返回上一頁或者跳轉到指定的路由頁面。
區別於
<HashRouter>
,有響應請求的伺服器時使用<BrowserRouter>
,使用的是靜態檔案的伺服器,則用<HashRouter>
。
簡單案例:
<BrowserRouter>
<Header />
<Route path="/" exact component={TimeLine}></Route>
<Route path="/timeline" component={TimeLine}></Route>
</BrowserRouter>
複製程式碼
5.1 BrowserRouter 語法
import { BrowserRouter } from 'react-router-dom'
<BrowserRouter
basename={optionalString}
forceRefresh={optionalBool}
getUserConfirmation={optionalFunc}
keyLength={optionalNumber}
>
<App/>
</BrowserRouter>
複製程式碼
5.2 BrowserRouter - basename
- 規則:
basename: string
為裡面的子目錄提供基礎路徑名,例如:
<BrowserRouter basename="/calendar">
<Link to="/today"/>
{/* 渲染為 <a href="/calendar/today"> */}
</BrowserRouter>
複製程式碼
5.3 BrowserRouter - getUserConfirmation
- 規則:
getUserConfirmation: function
用於確認導航的功能。
// 預設使用 window.confirm。
const getConfirmation = (message, callback) => {
const allowTransition = window.confirm(message)
callback(allowTransition)
}
<BrowserRouter getUserConfirmation={getConfirmation}/>
複製程式碼
5.4 BrowserRouter - forceRefresh
- 規則:
forceRefresh: bool
如果為 true,則路由器將在頁面導航中使用整頁重新整理
const supportsHistory = 'pushState' in window.history
<BrowserRouter forceRefresh={!supportsHistory}/>
複製程式碼
5.5 BrowserRouter - keyLength
- 規則:
keyLength: number
設定它裡面路由的 location.key
的長度。預設為 6。
key 的作用:點選同一個連結時,每次該路由下的 location.key都會改變,可以通過 key 的變化來重新整理頁面。
<BrowserRouter keyLength={12}/>
複製程式碼
六 HashRouter
使用 URL
的 hash
部分(即 window.location.hash
)的 <Router>
使 UI
與 URL
保持同步。
重要提示:Hash
歷史記錄不支援 location.key
或 location.state
。
import { HashRouter } from 'react-router-dom'
<HashRouter>
<App/>
</HashRouter>
複製程式碼
6.1 HashRouter - basename
- 規則:
basename: string
所有位置的基本 URL
,格式正確的基本名應該有一個前導斜線,但結尾不能有斜線。
<HashRouter basename="/calendar"/>
<Link to="/today"/>
{/* 渲染為 <a href="/calendar/today"> */}
複製程式碼
6.2 HashRouter - getUserConfirmation
- 規則:
getUserConfirmation: func
用於確認導航的功能。預設使用 window.confirm。
// this is the default behavior
const getConfirmation = (message, callback) => {
const allowTransition = window.confirm(message)
callback(allowTransition)
}
<HashRouter getUserConfirmation={getConfirmation}/>
複製程式碼
6.3 HashRouter - hashType
- 規則:
hashType: string
用於 window.location.hash
的編碼型別。可用的值是:
slash
- 建立#/
和的#/sunshine/lollipops
noslash
- 建立#
和的#sunshine/lollipops
hashbang
- 建立ajax crawlable
,如#!/
和#!/sunshine/lollipops
預設為 slash
。
七 Link
在應用程式周圍提供宣告式的,可訪問的導航。
7.1 Link - to
- 規則 1:
to: string
連結位置的字串表示,通過連線位置的路徑名,搜尋和 hash
屬性建立。
<Link to='/courses?sort=name'>字串形式跳轉</Link>
複製程式碼
- 規則 2:
to: object
一個可以具有以下任何屬性的物件:
pathname
: 表示要連結到的路徑的字串。search
: 表示查詢引數的字串形式。hash
: 放入網址的hash
,例如#a-hash
。state
: 狀態持續到location
。
<Link to={{
pathname: '/courses', // 基礎路徑
search: '?sort=name', // 匹配欄位
hash: '#the-hash', // 對應內鏈
state: { fromDashboard: true } // 未知
}}>
物件形式跳轉
</Link>
複製程式碼
7.2 Link - replace
- 規則:
replace: bool
如果為 true
,則單擊連結將替換歷史堆疊中的當前入口,而不是新增新入口。
<Link to="/courses" replace>替換當前 hash 路徑</Link>
複製程式碼
7.3 Link - other
還可以傳遞想要放在 <a>
上的屬性,例如標題,ID
、className
等。
<Link to="/test" id="myTest">測試 1</Link>
複製程式碼
八 NavLink
一個特殊版本的 Link,當它與當前 URL 匹配時,為其渲染元素新增樣式屬性。
- 高亮顯示首頁:
<NavLink to="/timeline" activeClassName="active">首頁</NavLink>
8.1 NavLink - activeClassName
- 規則:
activeClassName: string
要給出的元素的類處於活動狀態時。預設的給定類是 active
。它將與 className
屬性一起使用。
<NavLink
to="/faq"
activeClassName="selected"
>FAQs</NavLink>
複製程式碼
8.2 NavLink - activeStyle
- 規則:
activeStyle: object
當元素處於 active 時應用於元素的樣式。
<NavLink
to="/faq"
activeStyle={{
fontWeight: 'bold',
color: 'red'
}}
>FAQs</NavLink>
複製程式碼
8.3 NavLink - exact
- 規則:
exact: bool
如果為 true
,則僅在位置完全匹配時才應用 active
的類/樣式。
<NavLink
exact
to="/profile"
>Profile</NavLink>
複製程式碼
8.4 NavLink - isActive
- 規則:
isActive: function
一個為了確定連結是否處於活動狀態而新增額外邏輯的函式,如果你想做的不僅僅是驗證連結的路徑名與當前 URL 的 pathname 是否匹配,那麼應該使用它
// 如果連結不僅僅匹配 events/123,而是所有奇數連結都匹配
const oddEvent = (match, location) => {
if (!match) {
return false
}
const eventID = parseInt(match.params.eventID)
return !isNaN(eventID) && eventID % 2 === 1
}
<NavLink
to="/events/123"
isActive={oddEvent}
>Event 123</NavLink>
複製程式碼
九 MemoryRouter
<Router>
能在記憶體中儲存 URL
的歷史記錄(並不會對位址列進行讀寫)。很適合在測試環境和非瀏覽器環境中使用,例如 React Native。
<MemoryRouter>
<App/>
</MemoryRouter>
複製程式碼
9.1 MemoryRouter - initialEntries
- 規則:
initialEntries: array
history
棧中的一個 location
陣列。這些可能是具有 { pathname, search, hash, state }
或簡單的 URL
字串的完整地址物件。
<MemoryRouter
initialEntries={[ '/one', '/two', { pathname: '/three' } ]}
initialIndex={1}
>
<App/>
</MemoryRouter>
複製程式碼
9.2 MemoryRouter - initialIndex
- 規則:
initialIndex: number
在 initialEntries
陣列中的初始化地址索引。
9.3 MemoryRouter - getUserConfirmation
- 規則:
getUserConfirmation: function
用於確認導航的函式。在使用 <MemoryRouter>
時,直接使用 <Prompt>
,你必須使用這個選項。
9.4 MemoryRouter - keyLength
- 規則:
keyLength: number
location.key
的長度。預設為 6。
十 Redirect
渲染 <Redirect>
將使導航到一個新的地址。這個新的地址會覆蓋 history
棧中的當前地址,類似伺服器端(HTTP 3xx)的重定向。
我們可以設定某個路由重定向到另一個路由,例如下面即對 /
完全匹配重定向到 /timeline
頁面。
<Redirect from="/" to="/timeline" exact />
複製程式碼
10.1 Redirect - from
- 規則:
from: string
重定向 from
的路徑名。可以是任何 path-to-regexp
能夠識別的有效的 URL
路徑。
所有匹配的 URL
引數都提供給 to
中的模式。
必須包含在 to
中使用的所有引數。
to
未使用的其他引數將被忽略。
<Switch>
<Redirect from="/old-path" to="/new-path" />
<Route path="/new-path" component={Place} />
</Switch>
複製程式碼
10.2 Redirect - to
- 規則:
to: string
重定向到的 URL
,可以是任何 path-to-regexp
能夠理解有效 URL
路徑。
在 to
中使用的 URL
引數必須由 from
覆蓋。
<Redirect to="/somewhere/else" />
複製程式碼
- 規則:
to: object
重定向到的 location
,pathname
可以是任何 path-to-regexp
能夠理解的有效的 URL
路徑。
<Redirect
to={{
pathname: "/login",
search: "?utm=your+face",
state: { referrer: currentLocation }
}}
/>
複製程式碼
10.3 Redirect - push
- 規則:
push: bool
當 true
時,重定向會將新地址推入 history
中,而不是替換當前地址。
<Redirect push to="/somewhere/else" />
複製程式碼
10.4 Redirect - exact
- 規則:
exact: bool
完全匹配 from
。
十一 Route
只要應用程式位置與 Route 的路徑匹配,元件就會被渲染。
11.1 Route - component
只有當位置匹配時才會渲染的 React 元件。
<Route path="/user/:username" component={User}/>
const User = ({ match }) => {
return <h1>Hello {match.params.username}!</h1>
}
複製程式碼
11.2 Route - render
- 規則:
render: function
這允許方便的內聯渲染和包裹,而不是上面那種不想要的重新安裝的解釋
可以傳遞一個在位置匹配時呼叫的函式,而不是使用屬性為您建立新的 React element component
,該 render
屬性接收所有相同的 route props
的 component
渲染屬性。
// 行內編寫
<Route path="/home" render={() => <div>Home</div>}/>
// 包裹分開寫
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<FadeIn>
<Component {...props}/>
</FadeIn>
)}/>
)
<FadingRoute path="/cool" component={Something}/>
複製程式碼
11.3 Route - children
- 規則:
children: function
有時你需要渲染路徑是否匹配位置。在這些情況下,您可以使用函式 children
屬性,它的工作原理與渲染完全一樣,不同之處在於它是否存在匹配。
children
渲染道具接收所有相同的 route props
作為 component
和 render
方法,如果 Route
與 URL
不匹配,match
則為 null
,這允許你動態調整你的 UI
介面,基於路線是否匹配,如果路線匹配我們則新增一個 active
類。
<ul>
<ListItemLink to="/somewhere"/>
<ListItemLink to="/somewhere-else"/>
</ul>
const ListItemLink = ({ to, ...rest }) => (
<Route path={to} children={({ match }) => (
<li className={match ? 'active' : ''}>
<Link to={to} {...rest}/>
</li>
)}/>
)
複製程式碼
11.4 Route - path
- 規則:
path: string
任何 path-to-regexp
可以解析的有效的 URL
路徑
<Route path="/users/:id" component={User}/>
複製程式碼
11.5 Route - exact
- 規則:
exact: bool
如果為 true
,則只有在路徑完全匹配 location.pathname
時才匹配。
path | location.pathname | exact | matches? |
---|---|---|---|
/one | /one/two | true | no |
/one | /one/two | false | yes |
<Route exact path="/one" component={About}/>
複製程式碼
jsliang 個人經驗:
- 加了
exact
屬性後,會完全匹配路徑;如果沒有加,則二級路徑也會匹配當前路徑(例如/timeline/book
)。
<BrowserRouter>
<Route path="/" exact component={TimeLine}></Route>
<Route path="/timeline" component={TimeLine}></Route>
</BrowserRouter>
複製程式碼
- 我們可以動態設定
extra
的值,從而判斷是否需要載入某個元件。
const Home = () => <div>Home</div>;
const App = () => {
const someVariable = true;
return (
<Switch>
{/* these are good */}
<Route exact path="/" component={Home} />
<Route
path="/about"
render={props => <About {...props} extra={someVariable} />}
/>
{/* do not do this */}
<Route
path="/contact"
component={props => <Contact {...props} extra={someVariable} />}
/>
</Switch>
);
};
複製程式碼
11.6 Route - location
- 規則:
location: object
一個 <Route>
元素嘗試其匹配 path
到當前的歷史位置(通常是當前瀏覽器 URL)。但是,也可以通過location 一個不同 pathname
的匹配。
11.7 Route - sensitive
- 規則:
sensitive: bool
如果路徑區分大小寫,則為 true
,則匹配。
path | location.pathname | sensitive | matches? |
---|---|---|---|
/one | /one | true | yes |
/One | /one | true | no |
/One | /one | false | yes |
<Route sensitive path="/one" component={About}/>
複製程式碼
十二 Switch
渲染與該地址匹配的第一個子節點 <Route>
或者 <Redirect>
。
可以利用 <Switch>
做分組,即當有匹配時,匹配對應 path
對應的元件;如果沒有匹配,則匹配 NotFound
頁面。
<BrowserRouter>
<Header />
<Switch>
<Route path="/" exact component={TimeLine}></Route>
<Route path="/timeline" component={TimeLine}></Route>
<Route component={NotFound}></Route>
</Switch>
</BrowserRouter>
複製程式碼
十三 篇外一:history
history
是一個包,在你安裝 React Router 的時候,會作為它依賴包安裝到專案中,所以你可以直接使用 history
中的屬性和方法:
length
- (number
型別)history
堆疊的條目數action
- (string
型別) 當前的操作(push
,replace
,pop
)location
- (object
型別) 當前的位置。location
會具有以下屬性:pathname
- (string
型別) URL 路徑search
- (string
型別) URL 中的查詢字串hash
- (string
型別) URL 的雜湊片段state
- (object
型別) 提供給例如使用push(path, state)
操作將
location
放入堆疊時的特定location
狀態。只在瀏覽器和記憶體歷史中可用push(path, [state])
- (function
型別) 在history
堆疊新增一個新條目replace(path, [state])
- (function
型別) 替換在history
堆疊中的當前條目go(n)
- (function
型別) 將history
堆疊中的指標調整n
goBack()
- (function
型別) 等同於go(-1)
goForward()
- (function
型別) 等同於go(1)
block(prompt)
- (function
型別) 阻止跳轉
十四 篇外二:Code Splitting
隨著應用的增長,程式碼包會隨著生長。
到最後你會發現,你打包後的 js 檔案大地太多離譜。
所以,我們需要通過程式碼分割,依據不同的路由,載入不同的 js 檔案。
- 安裝 React Loadable:
npm i react-loadable -S
- 結合 React Router 和 React Loadable 進行 Code Spliting:
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Loadable from 'react-loadable';
const Loading = () => <div>Loading...</div>;
const Home = Loadable({
loader: () => import('./routes/Home'),
loading: Loading,
});
const About = Loadable({
loader: () => import('./routes/About'),
loading: Loading,
});
const App = () => (
<Router>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Router>
);
複製程式碼
- 打包專案:
npm run build
十五 篇外三:Scroll To Top
15.1 跳轉頁面後滾動到頂部
- 首先,在全域性 components 檔案中定義 ScrollToTop 資料夾,其中 index.js 內容為:
src/components/ScrollToTop/index.js
import { Component } from 'react';
import { withRouter } from 'react-router-dom';
class ScrollToTop extends Component {
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
window.scrollTo(0, 0)
}
}
render() {
return this.props.children
}
}
export default withRouter(ScrollToTop);
複製程式碼
- 然後,在 App.js 或者其他頁面中使用 ScrollToTop 功能:
src/App.js
import React, { Fragment } from 'react';
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
import ScrollToTop from './components/ScrollToTop';
import Header from './components/Header';
import TimeLine from './pages/TimeLine';
import NotFound from './pages/404';
function App() {
return (
<Fragment>
<BrowserRouter>
<Header />
<ScrollToTop>
<Switch>
<Redirect from="/" to="/timeline" exact />
<Route path="/timeline" component={TimeLine}></Route>
<Route component={NotFound}></Route>
</Switch>
</ScrollToTop>
</BrowserRouter>
</Fragment>
);
}
export default App;
複製程式碼
- 最後,我們切換路由的時候,頁面就會滾動到頂部。
15.2 頁面滾動到頂部
暫未實現
十六 篇外四:Redux
在專案中,我們更希望 React Router 和 React Redux 合併起來,這時候可以:
// before
export default connect(mapStateToProps)(Something)
// after
import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps)(Something))
複製程式碼
- 參考文獻:Redux Integration
十七 總結
如果你純粹過文件(官方文件,jsliang 的文件),你會覺得毫無趣味、了無生趣、乏味、沉悶……
所以,jsliang 的學法是:開啟了一個專案,邊翻閱文件,邊應用到專案中,並進行 Mark 標記,以便下次使用。
如此,該文件雖然完結了,但是仍未完結!完結的是我過完了官方文件,未完結的是 React Router 在我專案中可能有其他應用,需要我一一新增進來。
jsliang 廣告推送:
也許小夥伴想了解下雲伺服器
或者小夥伴想買一臺雲伺服器
或者小夥伴需要續費雲伺服器
歡迎點選 雲伺服器推廣 檢視!
jsliang 的文件庫 由 樑峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於github.com/LiangJunron…上的作品創作。
本許可協議授權之外的使用許可權可以從 creativecommons.org/licenses/by… 處獲得。