前面的話
路由用來分發請求。後端是提供服務的,所以它的路由是在找controller,前端是顯示頁面的,所以它的路由是在找component。本文將詳細介紹react-router-dom的內容
Router
Router是路由器元件的低階介面,通常會使用如下某個高階router來替代它
<BrowserRouter> <HashRouter> <MemoryRouter> <NativeRouter> <StaticRouter>
【BrowserRouter】
最常用的是BrowserRouter
import { BrowserRouter } from 'react-router-dom' <BrowserRouter basename={optionalString} forceRefresh={optionalBool} getUserConfirmation={optionalFunc} keyLength={optionalNumber} > <App/> </BrowserRouter>
1、basename: 當前位置的基準 URL。如果頁面部署在伺服器的二級(子)目錄,需要將 basename 設定到此子目錄。 正確的 URL 格式是前面有一個前導斜槓,但不能有尾部斜槓
<BrowserRouter basename="/calendar"/>
2、getUserConfirmation:當導航需要確認時執行的函式。預設使用 window.confirm
// 使用預設的確認函式 const getConfirmation = (message, callback) => { const allowTransition = window.confirm(message) callback(allowTransition) } <BrowserRouter getUserConfirmation={getConfirmation}/>
3、forceRefresh:當設定為 true 時,在導航的過程中整個頁面將會重新整理。 只有當瀏覽器不支援 HTML5 的 history API 時,才設定為 true
const supportsHistory = 'pushState' in window.history <BrowserRouter forceRefresh={!supportsHistory}/>
4、keyLength:location.key 的長度。預設是 6
<BrowserRouter keyLength={12}/>
5、BrowserRouter只能渲染單一子元素
Route
Route是react-router中最重要的元件,用來匹配請求並渲染相應元件
1、path 路徑的匹配值,可以包括以下幾種特殊符號
:paramName – 匹配一段位於 /、? 或 # 之後的 URL。 命中的部分將被作為一個引數
() – 在它內部的內容被認為是可選的
* – 匹配任意字元(非貪婪的)直到命中下一個字元或者整個 URL 的末尾,並建立一個 splat 引數
例子如下所示:
<Route path="/hello/:name"> // 匹配 /hello/michael 和 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello, /hello/michael 和 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
[注意]Route元件不能像普通元件一樣,以屬性的形式傳遞引數,但可以通過path屬性來傳遞。但一定要區分router後面的:_id或:id
'/category/:_id'
2、component 要顯示的元件
import { BrowserRouter as Router, Route } from 'react-router-dom' <Router> <div> <Route exact path="/" component={Home}/> <Route path="/news" component={NewsFeed}/> </div> </Router>
3、render 函式中return的值就是要顯示的內容
<Route path="/home" render={() => <div>Home</div>}/>
4、children與render的區別在於,不管有沒有匹配,都想顯示的內容
const ListItemLink = ({ to, ...rest }) => ( <Route path={to} children={({ match }) => ( <li className={match ? 'active' : ''}> <Link to={to} {...rest}/> </li> )}/> )
[注意]component/render/children只能三個選一個使用
【匹配規則】
預設地,路由進行寬鬆匹配。在下面例子中,路由匹配到/one時,既顯示元件A,也顯示元件B
<Route path="/one" component={A}/> <Route path="/one/two" component={B}/>
如果要進行確切匹配,則需要新增exact屬性。這樣,路由匹配到/one時,只顯示元件A
<Route exact path="/one" component={A}/> <Route path="/one/two" component={B}/>
還有一種是嚴格匹配,即斜槓也必須嚴格匹配。下面例子中,路由匹配到/one/時,會顯示元件A,但匹配到/one時,什麼都不會顯示
<Route strict path="/one/" component={A}/>
[注意]嚴格匹配並不是確切匹配。下面例子中,路由匹配到/one時,即顯示元件A,也顯示元件B
<Route strict path="/one" component={A}/> <Route path="/one/two" component={B}/>
如果要確切匹配,則需要
<Route exact strict path="/one" component={A}/>
但是,一般地,strict屬性很少使用
【屬性】
Route預設攜帶三個props:包括match、location、history
如果使用component,則使用this.props來獲取,如果是render,則在回撥函式中使用引數(props)=>{}來獲取
1、match
match包括以下屬性
params 鍵值對 isExact 是否確切匹配 path 路徑中設定的值 url URL中的path值
2、location
location中包含如下屬性
[注意]直接訪問location,而不是訪問history.location
{ key: 'ac3df4', // not with HashHistory! pathname: '/somewhere' search: '?some=search-string', hash: '#howdy', state: { [userDefined]: true } }
通過Link傳遞的state,可以在location中獲取到
[注意]剛開始時,或者直接重新整理瀏覽器,state是沒有值的,只有跳轉到該連結時,state才有值。再後來,重新整理也有值了
3、history
history包含如下屬性
length: history棧的長度
action: 當前的action
location: 當前的location物件
history包含如下方法
push() goBack() = go(-1) goForward() = go(1) go() 跳轉到 history棧中的哪個enter replace(path, [state]) 替換history棧中的當前entry push(path, [state]) 新增當前entry到history棧中
Redirect
Redirect將頁面導航到新位置,新位置將覆蓋history棧中的當前位置,類似於伺服器端的重定向(HTTP 3xx)
to屬性可以是一個字串,表示跳轉的地址
<Route exact path="/" render={() => ( loggedIn ? ( <Redirect to="/dashboard"/> ) : ( <PublicHomePage/> ) )}/>
to屬性也可以是一個物件
<Redirect to={{ pathname: '/login', search: '?utm=your+face', state: { referrer: currentLocation } }}/>
push屬性為true時,表示新增新記錄到history棧中,而不是替換當前記錄
<Redirect push to="/somewhere/else"/>
Link
Link是對a標籤的封裝,提供無重新整理的頁面跳轉。Link標籤主要的屬性是to屬性
1、一般地,to是一個字串
<Link to="/about">關於</Link>
2、也可以寫成物件的形式
<Link to={{ pathname: '/courses', search: '?sort=name', hash: '#the-hash', state: { fromDashboard: true } }}/>
[注意]在Link裡的子元件或同元件的點選事件,最好加上阻止預設行為和阻止冒泡
<Link> <div onclick={}></div> </Link> <Link onclick={}>
【NavLink】
NavLink相對於Link來說,增加了一些樣式屬性
activeClassName表示被匹配的a標籤的樣式名;activeStyle表示被匹配的a標籤的樣式
<NavLink to="/faq" activeClassName="selected" >FAQs</NavLink>
<NavLink to="/faq" activeStyle={{ fontWeight: 'bold', color: 'red' }} >FAQs</NavLink>
注意: link和history.push都不支援指向外網地址,如果要跳轉到外網,則需要使用window物件下的location物件
Switch
渲染Route或Redirect匹配到的第一個子元素
<Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> <Route path="/:user" component={User}/> <Route component={NoMatch}/> </Switch>
[注意]switch必須直接包括Route,中間不可包含div,否則不生效
跳轉
如果在實現邏輯跳轉,可使用如下程式碼實現
// utils/history.js import createBrowserHistory from 'history/createBrowserHistory' const customHistory = createBrowserHistory() export default customHistory
引用如下
import history from '@/utils/history' // 跳轉到首頁 history.push('/')
要特別注意的是,如果使用utils/history.js,需要使用Router history={history},而不是BrowserRouter
因為全域性只能有一個history例項。 使用import { BrowserRouter as Router } 語句,會自動建立一個history例項的,相當於有兩個例項,則會出現URL發生變化,重新整理頁面後,頁面才跳轉的情況
import { Router, Route, Switch, Redirect } from 'react-router-dom' import history from '@/utils/history' <Router history={history}> <Switch> <Route path="/login" component={Login} /> <Route path="/" render={props => {if (sessionStorage.getItem('token') && sessionStorage.getItem('user')) {return <Home {...props} /> } return <Redirect to="/login" /> }} /> </Switch> </Router>
【傳參】
history.push方法也可以攜帶引數,方法如下
history.push({ pathname: '/about', search: '?the=search', state: { some: 'state' } })
基礎案例
import React from 'react' import { BrowserRouter as Router, Route, Link } from 'react-router-dom' // 三個基礎呈現元件 const Home = () => ( <div> <h2>Home</h2> </div> ) const About = () => ( <div> <h2>About</h2> </div> ) const Topic = ({ match }) => ( <div> <h3>{match.params.topicId}</h3> </div> ) // 一個內嵌的元件 const Topics = ({ match }) => ( <div> <h2>Topics</h2> <ul> <li> <Link to={`${match.url}/rendering`}> Rendering with React </Link> </li> <li> <Link to={`${match.url}/components`}> Components </Link> </li> <li> <Link to={`${match.url}/props-v-state`}> Props v. State </Link> </li> </ul> <Route path={`${match.url}/:topicId`} component={Topic}/> <Route exact path={match.url} render={() => ( <h3>Please select a topic.</h3> )}/> </div> ) // 首頁元件 const BasicExample = () => ( <Router> <div> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/topics">Topics</Link></li> </ul> <hr/> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> <Route path="/topics" component={Topics}/> </div> </Router> ) export default BasicExample
exact
exact表示路由需要確切匹配,容易忽略的一點是,它與redux也有著非常密切的關係
下面的程式碼是常見的增刪改查的路由設定
<Switch> <Route path="/post/add" component={AddPost} /> <Route exact path="/post/:id" component={ShowPost} /> <Route path="/post/:id/update" component={UpdatePost} /> <Route path="/post/:id/delete" component={DeletePost} /> </Switch>
程式碼中,通向showPost的路由設定了exact。一般地,showPost通過fetch獲取了post,並儲存到store中的state.post中
如果此時點選到updatePost中,可以通過state.post來得到值。而如果在updatePost頁面直接重新整理的話,則state.post值為空
如果要確保頁面重新整理後仍然能夠取得值,則需要通過route中的location傳值
但是,這種方法有兩個缺陷。一個是不訪問showPost,而直接訪問UpdatePost不會獲得傳遞的值;另一個是直接在位址列中更改URL也不會獲取傳遞的值
2、去掉exact和switch,同時需要更改樣式和路由。使得path="/post/:id/update"時,可以同時匹配ShowPost和UpdatePost,且UpdatePost的頁面可以完全覆蓋ShowPost的頁面
<Route path="/posts/add" component={AddPost} /> <Route path="/post/:id" component={ShowPost} /> <Route path="/post/:id/update" component={UpdatePost} /> <Route path="/post/:id/delete" component={DeletePost} />
但是,由於這種方法對樣式的定製化需求較高,都需要設定為定位元素,且根據覆蓋關係來確定z-index。並且頁面尺寸都需要保持一致。可擴充套件性不強