react 路由實用篇 — React Router 4 入門及詳解
React Router 主要使用場景是單頁面富應用(SPA)。實現有兩種,一種是利用url的hash,就是常說的錨點(#),JavaScript通過hashChange事件來監聽url的改變;另一種是HTML5的History模式,它使url看起來像普通網站那樣,以“ / ”分割,沒有#,不過這種模式需要服務端支援,當url所指向的位置在服務端中目錄結構錯誤時,將報404。
React Router 引入有兩種方式,react-router 和 react-router-dom,大多數時候,我們使用的是react-router-dom包,因為它包括了一些便於路由實現的DOM元件,如NavLink。react-router-dom初始化語句如下:
$ npm install react-router-dom -S
要學會使用react-router-dom,必須理解好 HashRouter、BrowserRouter、NavLink、Link、Route、Switch、Redirect、 Prompt這些概念。
Router Demo:https://github.com/smallH/react-router-demo.git
那麼現在一步步來吧,下面是一個路由的實現程式碼:
<HashRouter basename="/"> <div> <div> <ul> <li><NavLink exact to="/" activeClassName="selected">First</NavLink></li> <li><NavLink to='/second/002' activeClassName="selected">Second</NavLink></li> <li><NavLink to={{ pathname: '/third', search: '?search=userinfo', state: { id: '003' } }} activeClassName="selected">Third</NavLink></li> </ul> </div> <div> <Switch> <Route exact path="/" component={First}></Route> <Route path="/second/:id" component={Second}></Route> <Route path="/third" component={Third}></Route> </Switch> </div> </div> </HashRouter>
該路由實現了單頁面的導航欄效果,內容包括了First、Second和Third三個頁面,當點選不同按鈕時會切換不同的頁面。
如果現在看不懂也沒關係,React Router說難也難,說簡單也簡單,關鍵還是要學會幾個常用的API ,所以接下來通過重點介紹React Router的API來入門學習。
API官網:https://reacttraining.com/react-router/
<BrowserRouter>
<BrowserRouter> 使用 HTML5 提供的 history API (pushState, replaceState 和 popstate 事件) 來保持 UI 和 URL 的同步。
import { BrowserRouter } from 'react-router-dom'
<BrowserRouter
basename={optionalString}
forceRefresh={optionalBool}
getUserConfirmation={optionalFunc}
keyLength={optionalNumber}
>
<App/>
</BrowserRouter>
-
basename: string
所有位置的基準 URL。如果你的應用程式部署在伺服器的子目錄,則需要將其設定為子目錄。basename 的正確格式是前面有一個前導斜槓,但不能有尾部斜槓。
<BrowserRouter basename="/calendar">
<Link to="/today" /> // 最終渲染為 <a href="/calendar/today" />
</BrowserRouter>
-
forceRefresh: bool
如果為true,在導航的過程中整個頁面將會重新整理。一般情況下,只有在不支援 HTML5 history API 的瀏覽器中使用此功能。
const supportsHistory = 'pushState' in window.history;
<BrowserRouter forceRefresh={!supportsHistory} />
-
getUserConfirmation: func
用於確認導航的函式,預設使用window.confirm。例如,當從 /a 導航至 /b 時,會使用預設的 confirm 函式彈出一個提示,使用者點選確定後才進行導航,否則不做任何處理。
const getConfirmation = (message, callback) => {
const allowTransition = window.confirm(message);
callback(allowTransition); // 回撥詢問結果
}
<BrowserRouter getUserConfirmation={getConfirmation('是否允許使用路由?', myCallBack)} />
-
keyLength: number
location.key 的長度,預設為 6,每個頁面都有一個location.key。
<BrowserRouter keyLength={12} />
-
children: node
呈現的單個子元素或DOM元件。
<HashRouter>
<HashRouter> 使用 URL 的 hash 部分(即 window.location.hash)來保持 UI 和 URL 的同步。
import { HashRouter } from 'react-router-dom';
<HashRouter>
<App />
</HashRouter>
-
basename: string
同上 <BrowserRouter>
-
getUserConfirmation: func
同上 <BrowserRouter>
-
hashType: string
window.location.hash使用的hash型別,有如下幾種:
- slash - 後面跟一個斜槓,例如 #/ 和 #/sunshine/lollipops
- noslash - 後面沒有斜槓,例如 # 和 #sunshine/lollipops
- hashbang - Google 風格的 ajax crawlable,例如 #!/ 和 #!/sunshine/lollipops
預設為 slash。
-
children: node
呈現的單個子元素或DOM元件。
(貼士)關於選擇HashRouter還是BrowserRouter?
雖然官網中推薦使用BrowserRouter,但出於個人實際專案開發經驗及相容性的考慮,還是建議使用HashRouter。首先BrowserRouter是基於HTML5的,對舊式的瀏覽器不一定支援,其次,BrowserRouter 在build後,打包出來的檔案路由是對伺服器的目錄是有嚴格要求的,比如把打包出來的檔案放到了伺服器的root/project/目錄下,若在BrowserRouter 中沒有配置基準 URL,將會訪問不到頁面資源。有人說為什麼在本地開發是沒有問題的?那是因為在本地開發時,webpack.config.js中使用 webpack-dev-server 已經做了配置。
<Link>
為你的應用提供宣告式的、可訪問的導航連結。
import { Link } from 'react-router-dom';
<Link to="/about">About</Link>
-
to: string
一個字串形式的連結地址,通過 pathname
、search
和 hash
屬性建立,其中search
和 hash可以為空
。
<Link to='/courses?sort=name#the-hash' />
-
to: object
一個物件形式的連結地址,可以具有以下任何屬性:
- pathname - 要連結到的路徑
- search - 查詢引數
- hash - URL 中的 hash,例如 #the-hash
- state - 儲存到 location 中的額外狀態資料
<Link to={{
pathname: '/courses',
search: '?sort=name',
hash: '#the-hash',
state: {
fromDashboard: true
}
}} />
-
replace: bool
當設定為 true
時,點選連結後將替換歷史堆疊中的當前條目,而不是新增新條目。預設為 false
。
<Link to="/courses" replace />
比如,從頁面 '/a' 跳到 '/b',再跳到帶replace屬性的'/c',此時從'/c'點選瀏覽器的返回上一頁,將直接返回到'/a' ,'/b' 會被忽略。
-
innerRef: func
允許訪問元件的底層引用。
const refCallback = node => {
// node 指向最終掛載的 DOM 元素,在解除安裝時為 null
}
<Link to="/" innerRef={refCallback} />
-
others
還可以傳遞一些其它屬性,例如 title
、id
或 className
等。
<Link to="/" className="nav" title="a title">About</Link>
<NavLink>
一個特殊版本的 <Link>
,它會在與當前 URL 匹配時為其呈現元素新增樣式屬性。
import { NavLink } from 'react-router-dom';
<NavLink to="/about">About</NavLink>
-
activeClassName: string
當元素處於啟用狀態時應用的類,預設為 active
。它將與 className
屬性一起使用。
<NavLink to="/faq" activeClassName="selected">FAQs</NavLink>
-
activeStyle: object
當元素處於啟用狀態時應用的樣式。
const activeStyle = {
fontWeight: 'bold',
color: 'red'
};
<NavLink to="/faq" activeStyle={activeStyle}>FAQs</NavLink>
-
exact: bool
如果為 true
,則只有在位置完全匹配時才應用啟用類/樣式。
<NavLink exact to="/profile">Profile</NavLink>
-
isActive: func
新增額外邏輯以確定連結是否處於啟用狀態的函式。如果你要做的不僅僅是驗證連結的路徑名與當前 URL 的路徑名相匹配,那麼應該使用它。
// 只有當事件 id 為奇數時才考慮啟用
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>
-
strict: bool
如果為 true
,則在確定位置是否與當前 URL 匹配時,將考慮位置的路徑名後面的斜槓。
<NavLink strict to="/one/">Events</NavLink>
<Route>
<Route>是指與<Link>的to設定的路徑匹配時,顯示其指定的元件。
注意,<Route path='/'>表示起始頁,它與所有的<Link>都會匹配到,如:
<HashRouter basename="/">
<div>
<div>
<ul>
<li><NavLink to="/" activeClassName="selected">First</NavLink></li>
<li><NavLink to='/second' activeClassName="selected">Second</NavLink></li>
</ul>
</div>
<div>
<Route path="/" component={First}></Route>
<Route path="/second" component={Second}></Route>
</div>
</div>
</HashRouter>
當點選Second時,元件First、Second都會顯示出來
這明顯不是我們想要的結果,這時就需要用到exact和<switch>實現嚴格模式匹配和只渲染一次。
Route有三種元件的渲染方式:
- <Route component>
- <Route render>
- <Route children>
三種渲染方式都將提供相同的Route Props:
- match
- location
- history
如元件裡可以通過this.props.match獲取匹配資訊;通過this.props.history.push(path)實現路由頁面跳轉,若需傳遞引數。path可以換成Object,引數與<Link>中to傳遞物件引數相同。
-
component
指定只有當位置匹配時才會渲染的 React 元件,該元件會接收 route props 作為屬性。
const User = ({ match }) => {
return <h1>Hello {match.params.username}!</h1>
}
<Route path="/user/:username" component={User} />
當你使用 component 時,Router將根據指定的元件,使用 React.createElement 建立一個新的 React 元素。
-
render: func
使用 render 可以方便地進行內聯渲染和包裝,而無需進行上文解釋的不必要的元件重灌。這種寫法有一個好處就是可以很方便地在切換頁面時實現一些動畫效果。
// AnimateComp 為切換動畫效果元件
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<AnimateComp>
<Component {...props} />
</AnimateComp>
)} />
)
<FadingRoute path="/second" component={Second} />
-
children: func
與render效果一樣,只是寫法不同。
<Route path="/second" children={({ match, ...rest }) => (
<AnimateComp>
{match && <Second {...rest}/>}
</AnimateComp>
)}/>
-
path: string
有效的 URL 路徑,沒有定義 path 的 <Route> 總是會被匹配。
<Route path="/users/:id" component={User} />
-
exact: bool
嚴格匹配,如果為 true
,則只有在 path
完全匹配 location.pathname
時才匹配,即Route中的path和Link中的to的路徑必須一致時,該Route才會被渲染。
-
strict: bool
同上<NavLink> srict效果。
-
sensitive: bool
如果為 true
,進行匹配時將區分大小寫。
<Switch>
用於渲染與路徑匹配的第一個子 <Route>
或 <Redirect>
。<Switch> 只會渲染一個路由。相反,僅僅定義一系列 <Route> 時,每一個與路徑匹配的 <Route> 都將包含在渲染範圍內。考慮如下程式碼:
<Route path="/about" component={About} />
<Route path="/:user" component={User} />
<Route component={NoMatch} />
如果 URL 是 /about,那麼 <About>、<User> 和 <NoMatch> 將全部渲染,因為它們都與路徑匹配,其中第二個 '/:user' 等同於 '/*' ,第三個沒有設定path,可以與任何路徑匹配。這種方式通過設計的,允許我們以很多方式將 <Route> 組合成我們的應用程式,例如側邊欄和麵包屑、引導標籤等。
但是,有時候我們只想選擇一個 <Route> 來呈現。比如我們在 URL 為 /about 時不想匹配 /:user(或者顯示我們的 404 頁面),這該怎麼實現呢?以下就是如何使用 <Switch> 做到這一點:
import { Switch, Route } from 'react-router';
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/:user" component={User} />
<Route component={NoMatch} />
</Switch>
現在,當我們在 /about 路徑時,<Switch> 將開始尋找匹配的 <Route>。我們知道,<Route path="/about" /> 將會被正確匹配,這時 <Switch> 會停止查詢匹配項並立即呈現 <About>。同樣,如果我們在 /michael 路徑時,那麼 <User> 會呈現。
<Redirect>
使用 <Redirect>
會導航到一個新的位置。新的位置將覆蓋歷史堆疊中的當前條目,例如伺服器端重定向(HTTP 3xx)。
import { Route, Redirect } from 'react-router-dom';
<Route exact path="/" render={() => (
loggedIn ? (
<Redirect to="/dashboard" />
) : (
<PublicHomePage />
)
)} />
-
to: string
要重定向到的 URL。
<Redirect to="/somewhere/else" />
-
to: object
要重定向到的物件。
<Redirect to={{
pathname: '/login',
search: '?utm=your+face',
state: {
referrer: currentLocation
}
}} />
上例中的 state 物件可以在重定向到的元件中通過 this.props.location.state 進行訪問。
-
push: bool
如果為 true
,重定向會將新的位置推入歷史記錄,而不是替換當前條目。
<Redirect push to="/somewhere/else" />
-
from: string
只能在 <Switch>
元件內使用 <Redirect from>
,以匹配一個位置。
<Switch>
<Redirect from='/old-path' to='/new-path' />
<Route path='/new-path' component={Place} />
</Switch>
-
exact: bool
當為true時,表示嚴格模式匹配。
-
strict: bool
同上<NavLink>。
<Prompt>
用於在位置跳轉之前給予使用者一些確認資訊。當你的應用程式進入一個應該阻止使用者導航的狀態時(比如表單只填寫了一半),彈出一個提示。
import { Prompt } from 'react-router-dom';
<Prompt
when={formIsHalfFilledOut}
message="你確定要離開當前頁面嗎?"
/>
-
message: string
當用戶試圖離開某個位置時彈出的提示資訊。
<Prompt message="你確定要離開當前頁面嗎?" />
-
message: func
將在使用者試圖導航到下一個位置時呼叫。需要返回一個字串以向用戶顯示提示,或者返回 true
以允許直接跳轉。
<Prompt message={location => {
const isApp = location.pathname.startsWith('/app');
return isApp ? `你確定要跳轉到${location.pathname}嗎?` : true;
}} />
-
when: bool
在應用程式中,你可以始終渲染 <Prompt>
元件,並通過設定 when={true}
或 when={false}
以阻止或允許相應的導航,而不是根據某些條件來決定是否渲染 <Prompt>
元件。
<Prompt when={true} message="你確定要離開當前頁面嗎?" />