React Router 4.0 (以下簡稱 RR4) 已經(jīng)正式發(fā)布,它遵循React的設(shè)計(jì)理念,即萬物皆組件。所以 RR4 只是一堆 提供了導(dǎo)航功能的組件(還有若干對(duì)象和方法),具有聲明式(聲明式編程簡單來講就是你只需要關(guān)心做什么,而無需關(guān)心如何去做,好比你寫 React 組件,只需要 render 出你想要的組件,至于組件是如何實(shí)現(xiàn)的是 React 要處理的事情。),可組合性的特點(diǎn)。
v4 中文博客介紹
v3 如何遷徙到 v4
v4 代碼分割(異步加載)
v4 代碼分割輔助庫 react-loadable
v4 完全指南
RR4 本次采用單代碼倉庫模型架構(gòu)(monorepo),這意味者這個(gè)倉庫里面有若干相互獨(dú)立的包,分別是:
-
react-routerReact Router 核心 -
react-router-dom用于 DOM 綁定的 React Router -
react-router-native用于 React Native 的 React Router -
react-router-reduxReact Router 和 Redux 的集成 -
react-router-config靜態(tài)路由配置的小助手
本文主要討論在 web app 中如何使用使用 RR4。
引用
react-router 還是 react-router-dom?
在 React 的使用中,我們一般要引入兩個(gè)包,react 和 react-dom,那么 react-router 和 react-router-dom 是不是兩個(gè)都要引用呢?
非也,坑就在這里。他們兩個(gè)只要引用一個(gè)就行了,不同之處就是后者比前者多出了 <Link> <BrowserRouter> 這樣的 DOM 類組件。
因此我們只需引用 react-router-dom 這個(gè)包就行了。當(dāng)然,如果搭配 redux ,你還需要使用 react-router-redux。
what is the diff between react-router-dom & react-router?
組件
<BrowserRouter>
一個(gè)使用了 HTML5 history API 的高階路由組件,保證你的 UI 界面和 URL 保持同步。此組件擁有以下屬性:
basename: string
作用:為所有位置添加一個(gè)基準(zhǔn)URL
使用場景:假如你需要把頁面部署到服務(wù)器的二級(jí)目錄,你可以使用 basename 設(shè)置到此目錄。
<BrowserRouter basename="/minooo" />
<Link to="/react" /> // 最終渲染為 <a href="/minooo/react">
getUserConfirmation: func
作用:導(dǎo)航到此頁面前執(zhí)行的函數(shù),默認(rèn)使用 window.confirm
使用場景:當(dāng)需要用戶進(jìn)入頁面前執(zhí)行什么操作時(shí)可用,不過一般用到的不多。
const getConfirmation = (message, callback) => {
const allowTransition = window.confirm(message)
callback(allowTransition)
}
<BrowserRouter getUserConfirmation={getConfirmation('Are you sure?', yourCallBack)} />
forceRefresh: bool
作用:當(dāng)瀏覽器不支持 HTML5 的 history API 時(shí)強(qiáng)制刷新頁面。
使用場景:同上。
const supportsHistory = 'pushState' in window.history
<BrowserRouter forceRefresh={!supportsHistory} />
keyLength: number
作用:設(shè)置它里面路由的 location.key 的長度。默認(rèn)是6。(key的作用:點(diǎn)擊同一個(gè)鏈接時(shí),每次該路由下的 location.key都會(huì)改變,可以通過 key 的變化來刷新頁面。)
使用場景:按需設(shè)置。
<BrowserRouter keyLength={12} />
children: node
作用:渲染唯一子元素。
使用場景:作為一個(gè) Reac t組件,天生自帶 children 屬性。
<HashRouter>
Hash history 不支持 location.key 和 location.state。另外由于該技術(shù)只是用來支持舊版瀏覽器,因此更推薦大家使用 BrowserRouter,此API不再作多余介紹。
<Route>
<Route> 也許是 RR4 中最重要的組件了,重要到你必須理解它,學(xué)會(huì)它,用好它。它最基本的職責(zé)就是當(dāng)頁面的訪問地址與 Route 上的 path 匹配時(shí),就渲染出對(duì)應(yīng)的 UI 界面。
<Route> 自帶三個(gè) render method 和三個(gè) props 。
render methods 分別是:
<Route component><Route render>-
<Route children>
每種 render method 都有不同的應(yīng)用場景,同一個(gè)<Route>應(yīng)該只使用一種 render method ,大部分情況下你將使用component。
props 分別是:
matchlocation-
history
所有的 render method 無一例外都將被傳入這些 props。
component
只有當(dāng)訪問地址和路由匹配時(shí),一個(gè) React component 才會(huì)被渲染,此時(shí)此組件接受 route props (match, location, history)。
當(dāng)使用 component 時(shí),router 將使用 React.createElement 根據(jù)給定的 component 創(chuàng)建一個(gè)新的 React 元素。這意味著如果你使用內(nèi)聯(lián)函數(shù)(inline function)傳值給 component 將會(huì)產(chǎn)生不必要的重復(fù)裝載。對(duì)于內(nèi)聯(lián)渲染(inline rendering), 建議使用 render prop。
<Route path="/user/:username" component={User} />
const User = ({ match }) => {
return <h1>Hello {match.params.username}!</h1>
}
render: func
此方法適用于內(nèi)聯(lián)渲染,而且不會(huì)產(chǎn)生上文說的重復(fù)裝載問題。
// 內(nèi)聯(lián)渲染
<Route path="/home" render={() => <h1>Home</h1} />
// 包裝 組合
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<FadeIn>
<Component {...props} />
</FaseIn>
)} />
)
<FadingRoute path="/cool" component={Something} />
children: func
有時(shí)候你可能只想知道訪問地址是否被匹配,然后改變下別的東西,而不僅僅是對(duì)應(yīng)的頁面。
<ul>
<ListItemLink to="/somewhere" />
<ListItemLink to="/somewhere-ele" />
</ul>
const ListItemLink = ({ to, ...rest }) => (
<Route path={to} children={({ match }) => (
<li className={match ? 'active' : ''}>
<Link to={to} {...rest} />
</li>
)}
)
path: string
任何可以被 path-to-regexp解析的有效 URL 路徑
<Route path="/users/:id" component={User} />
如果不給path,那么路由將總是匹配。
exact: bool
如果為 true,path 為 '/one' 的路由將不能匹配 '/one/two',反之,亦然。
strict: bool
對(duì)路徑末尾斜杠的匹配。如果為 true。path 為 '/one/' 將不能匹配 '/one' 但可以匹配 '/one/two'。
如果要確保路由沒有末尾斜杠,那么 strict 和
exact 都必須同時(shí)為 true
<Link>
為你的應(yīng)用提供聲明式,無障礙導(dǎo)航。
to: string
作用:跳轉(zhuǎn)到指定路徑
使用場景:如果只是單純的跳轉(zhuǎn)就直接用字符串形式的路徑。
<Link to="/courses" />
to: object
作用:攜帶參數(shù)跳轉(zhuǎn)到指定路徑
作用場景:比如你點(diǎn)擊的這個(gè)鏈接將要跳轉(zhuǎn)的頁面需要展示此鏈接對(duì)應(yīng)的內(nèi)容,又比如這是個(gè)支付跳轉(zhuǎn),需要把商品的價(jià)格等信息傳遞過去。
<Link to={{
pathname: '/course',
search: '?sort=name',
state: { price: 18 }
}} />
replace: bool
為 true 時(shí),點(diǎn)擊鏈接后將使用新地址替換掉上一次訪問的地址,什么意思呢,比如:你依次訪問 '/one' '/two' '/three' ’/four' 這四個(gè)地址,如果回退,將依次回退至 '/three' '/two' '/one' ,這符合我們的預(yù)期,假如我們把鏈接 '/three' 中的 replace 設(shè)為 true 時(shí)。依次點(diǎn)擊 one two three four 然后再回退會(huì)發(fā)生什么呢?會(huì)依次退至 '/three' '/one'! 為此我做了個(gè)在線 demo,大家可以調(diào)試體會(huì)一下 !
另外你能想到這個(gè) prop 的用途是什么呢?有人說在用 路由 做選項(xiàng)卡時(shí)候會(huì)用到。歡迎留言討論!
<NavLink>
這是 <Link> 的特殊版,顧名思義這就是為頁面導(dǎo)航準(zhǔn)備的。因?yàn)閷?dǎo)航需要有 “激活狀態(tài)”。
activeClassName: string
導(dǎo)航選中激活時(shí)候應(yīng)用的樣式名,默認(rèn)樣式名為 active
<NavLink
to="/about"
activeClassName="selected"
>MyBlog</NavLink>
activeStyle: object
如果不想使用樣式名就直接寫style
<NavLink
to="/about"
activeStyle={{ color: 'green', fontWeight: 'bold' }}
>MyBlog</NavLink>
exact: bool
若為 true,只有當(dāng)訪問地址嚴(yán)格匹配時(shí)激活樣式才會(huì)應(yīng)用
strict: bool
若為 true,只有當(dāng)訪問地址后綴斜杠嚴(yán)格匹配(有或無)時(shí)激活樣式才會(huì)應(yīng)用
isActive: func
決定導(dǎo)航是否激活,或者在導(dǎo)航激活時(shí)候做點(diǎn)別的事情。不管怎樣,它不能決定對(duì)應(yīng)頁面是否可以渲染。
<Switch>
只渲染出第一個(gè)與當(dāng)前訪問地址匹配的 <Route> 或 <Redirect>。
思考如下代碼,如果你訪問 /about,那么組件 About User Nomatch 都將被渲染出來,因?yàn)樗麄儗?duì)應(yīng)的路由與訪問的地址 /about 匹配。這顯然不是我們想要的,我們只想渲染出第一個(gè)匹配的路由就可以了,于是 <Switch> 應(yīng)運(yùn)而生!
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
也許你會(huì)問,為什么 RR4 機(jī)制里不默認(rèn)匹配第一個(gè)符合要求的呢,答:這種設(shè)計(jì)允許我們將多個(gè) <Route> 組合到應(yīng)用程序中,例如側(cè)邊欄(sidebars),面包屑 等等。
另外,<Switch> 對(duì)于轉(zhuǎn)場動(dòng)畫也非常適用,因?yàn)楸讳秩镜穆酚珊颓耙粋€(gè)被渲染的路由處于同一個(gè)節(jié)點(diǎn)位置!
<Fade>
<Switch>
{/* 用了Switch 這里每次只匹配一個(gè)路由,所有只有一個(gè)節(jié)點(diǎn)。 */}
<Route/>
<Route/>
</Switch>
</Fade>
<Fade>
<Route/>
<Route/>
{/* 不用 Switch 這里可能就會(huì)匹配多個(gè)路由了,即便匹配不到,也會(huì)返回一個(gè)null,使動(dòng)畫計(jì)算增加了一些麻煩。 */}
</Fade>
children: node
<Switch> 下的子節(jié)點(diǎn)只能是 <Route> 或 <Redirect> 元素。只有與當(dāng)前訪問地址匹配的第一個(gè)子節(jié)點(diǎn)才會(huì)被渲染。<Route> 元素用它們的 path 屬性匹配,<Redirect> 元素使用它們的 from 屬性匹配。如果沒有對(duì)應(yīng)的 path 或 from,那么它們將匹配任何當(dāng)前訪問地址。
<Redirect>
<Redirect> 渲染時(shí)將導(dǎo)航到一個(gè)新地址,這個(gè)新地址覆蓋在訪問歷史信息里面的本該訪問的那個(gè)地址。
to: string
重定向的 URL 字符串
to: object
重定向的 location 對(duì)象
push: bool
若為真,重定向操作將會(huì)把新地址加入到訪問歷史記錄里面,并且無法回退到前面的頁面。
from: string
需要匹配的將要被重定向路徑。
Prompt
當(dāng)用戶離開當(dāng)前頁面前做出一些提示。
message: string
當(dāng)用戶離開當(dāng)前頁面時(shí),設(shè)置的提示信息。
<Prompt message="確定要離開?" />
message: func
當(dāng)用戶離開當(dāng)前頁面時(shí),設(shè)置的回掉函數(shù)
<Prompt message={location => (
`Are you sue you want to go to ${location.pathname}?`
)} />
when: bool
通過設(shè)置一定條件要決定是否啟用 Prompt
對(duì)象和方法
history
histoty 是 RR4 的兩大重要依賴之一(另一個(gè)當(dāng)然是 React 了),在不同的 javascript 環(huán)境中, history 以多種能夠行駛實(shí)現(xiàn)了對(duì)會(huì)話(session)歷史的管理。
我們會(huì)經(jīng)常使用以下術(shù)語:
- "browser history" - history 在 DOM 上的實(shí)現(xiàn),用于支持 HTML5 history API 的瀏覽器
- "hash history" - history 在 DOM 上的實(shí)現(xiàn),用于舊版瀏覽器。
- "memory history" - history 在內(nèi)存上的實(shí)現(xiàn),用于測試或非 DOM 環(huán)境(例如 React Native)。
history 對(duì)象通常具有以下屬性和方法:
- length: number 瀏覽歷史堆棧中的條目數(shù)
- action: string 路由跳轉(zhuǎn)到當(dāng)前頁面執(zhí)行的動(dòng)作,分為 PUSH, REPLACE, POP
- location: object 當(dāng)前訪問地址信息組成的對(duì)象,具有如下屬性:
- pathname: string URL路徑
- search: string URL中的查詢字符串
- hash: string URL的 hash 片段
- state: string 例如執(zhí)行 push(path, state) 操作時(shí),location 的 state 將被提供到堆棧信息里,state 只有在 browser 和 memory history 有效。
- push(path, [state]) 在歷史堆棧信息里加入一個(gè)新條目。
- replace(path, [state]) 在歷史堆棧信息里替換掉當(dāng)前的條目
- go(n) 將 history 堆棧中的指針向前移動(dòng) n。
- goBack() 等同于 go(-1)
- goForward 等同于 go(1)
- block(prompt) 阻止跳轉(zhuǎn)
history 對(duì)象是可變的,因?yàn)榻ㄗh從 <Route> 的 prop 里來獲取 location,而不是從 history.location 直接獲取。這樣可以保證 React 在生命周期中的鉤子函數(shù)正常執(zhí)行,例如以下代碼:
class Comp extends React.Component {
componentWillReceiveProps(nextProps) {
// locationChanged
const locationChanged = nextProps.location !== this.props.location
// 錯(cuò)誤方式,locationChanged 永遠(yuǎn)為 false,因?yàn)閔istory 是可變的
const locationChanged = nextProps.history.location !== this.props.history.location
}
}
location
location 是指你當(dāng)前的位置,將要去的位置,或是之前所在的位置
{
key: 'sdfad1'
pathname: '/about',
search: '?name=minooo'
hash: '#sdfas',
state: {
price: 123
}
}
在以下情境中可以獲取 location 對(duì)象
- 在
Route component中,以 this.props.location 獲取 - 在
Route render中,以 ({location}) => () 方式獲取 - 在
Route children中,以 ({location}) => () 方式獲取 - 在
withRouter中,以 this.props.location 的方式獲取
location 對(duì)象不會(huì)發(fā)生改變,因此可以在生命周期的回調(diào)函數(shù)中使用 location 對(duì)象來查看當(dāng)前頁面的訪問地址是否發(fā)生改變。這種技巧在獲取遠(yuǎn)程數(shù)據(jù)以及使用動(dòng)畫時(shí)非常有用
componentWillReceiveProps(nextProps) {
if (nextProps.location !== this.props.location) {
// 已經(jīng)跳轉(zhuǎn)了!
}
}
可以在不同情境中使用 location:
<Link to={location} /><NaviveLink to={location} /><Redirect to={location />- history.push(location)
- history.replace(location)
match
match 對(duì)象包含了 <Route path> 如何與 URL 匹配的信息,具有以下屬性:
- params: object 路徑參數(shù),通過解析 URL 中的動(dòng)態(tài)部分獲得鍵值對(duì)
- isExact: bool 為 true 時(shí),整個(gè) URL 都需要匹配
- path: string 用來匹配的路徑模式,用于創(chuàng)建嵌套的 <Route>
- url: string URL 匹配的部分,用于嵌套的 <Link>
在以下情境中可以獲取 match 對(duì)象
- 在
Route component中,以 this.props.match獲取 - 在
Route render中,以 ({match}) => () 方式獲取 - 在
Route children中,以 ({match}) => () 方式獲取 - 在
withRouter中,以 this.props.match的方式獲取 - matchPath 的返回值
當(dāng)一個(gè) Route 沒有 path 時(shí),它會(huì)匹配一切路徑。