再也不提倡中心化路由!嵌套路由再也不是
{ props.children }
的用法了。每一個路由都是一個 React 組件。
在 web 端使用,只須要導入這個包就能夠了,由於它從 react-router
中拿過來了不少東西。react
// @types/react-router-dom/index.d.ts export { …… } from 'react-router';
而後看看經常使用的有哪些功能web
理解爲路由容器,被包裹在裏面的子組件就可使用本身定義的路由組件了。編程
// index.tsx import React from 'react'; import ReactDOM from 'react-dom'; import { HashRouter } from 'react-router-dom'; import App from './App'; ReactDOM.render( <HashRouter> <App /> </HashRouter> , document.getElementById('root') );
路由組件,路由匹配時這個位置被渲染成相應的內容。react-router
path
須要匹配的路徑component
匹配成功渲染的組件exact
是否嚴格匹配<Route path="/home" exact component={ Home } />
這個例子中,只有當路徑爲 /home
時纔會匹配。若沒有 exact
屬性,那麼當路徑爲 /home
時,/
、/home
這兩個路由組件都會被渲染。dom
v4 以上版本再也不支持 { props.children }
的方式進行嵌套路由,而是直接把子路由組件放在父組件中須要渲染的位置。函數
// App.tsx <div> <Route path="/" component={ Dashboard } /> </div> // Dashboard.tsx <div> <Header /> <Route path="/home" component={ Home } /> <Route path="/other" component={ Other } /> </div>
這樣的嵌套路由寫法,須要保證父組件與子組件有相同的路由前綴(/
),且父組件沒有 exact
屬性。(目的是先渲染父組件,再匹配父組件內部定義的子路由組件)url
和其餘路由插件同樣,使用冒號配置動態路由。插件
// Dashboard.tsx <div> <Header /> <Route path="/home" component={ Home } /> <Route path="/other" exact component={ Other } /> <Route path="/other/:id" component={ OtherDetail } /> </div>
/other/1
會匹配 /other
和 /other/:id
這兩個路由組件,根據實際狀況對 /other
路由組件設置 exact
屬性。code
// @types/react-router/index.d.ts export function useParams<Params extends { [K in keyof Params]?: string } = {}>(): { [p in keyof Params]: string };
useParams()
方法返回的是一個對象,直接取屬性 TS 會提示空對象中不存在這個屬性。按照 TS 的規範,能夠在動態路由組件中,定義一個接口約定路由傳遞的參數。component
// OtherDetail.tsx import React from 'react'; import { useParams } from 'react-router-dom'; interface RouteParams { id: string } export default () => { const params = useParams<RouteParams>(); return ( <div> 動態路由:{ params.id } </div> ) }
路由組件的 props
數據類型爲 RouteComponentProps
// @types/react-router/index.d.ts export interface RouteComponentProps<Params extends { [K in keyof Params]?: string } = {}, C extends StaticContext = StaticContext, S = H.LocationState> { history: H.History; location: H.Location<S>; match: match<Params>; staticContext?: C; }
其中 match
屬性會用的比較多
// @types/react-router/index.d.ts export interface match<Params extends { [K in keyof Params]?: string } = {}> { params: Params; isExact: boolean; path: string; url: string; }
在動態路由 /other/1
中,props.match.url
的值爲 /other/1
,props.match.path
的值爲 /other/:id
。獲取 props.match.params
中的屬性仍然須要告訴 TS 有哪些屬性。
import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; interface RouteParams { id: string } export default (props: RouteComponentProps<RouteParams>) => { return ( <div> 動態路由:{ props.match.params.id } </div> ) }
上面說到可使用 props
獲取到與路由相關的信息,其中包括了 match
、params
等,可使用 props.match
獲取路由的匹配信息。也可使用 useRouteMatch
方法。
// @types/react-router/index.d.ts export function useRouteMatch<Params extends { [K in keyof Params]?: string } = {}>( path?: string | string[] | RouteProps, ): match<Params> | null;
注意 useRouteMatch()
的返回值多是 null
,不能簡單的經過 match.*
的形式訪問。
// Other.tsx import React from 'react'; import { useRouteMatch } from 'react-router'; export default () => { const match = useRouteMatch(); return ( <div>路由路徑:{ match && match.url }</div> ) }
useLocation
和 useHistory
的用法相似。
Switch
只會匹配子組件中的第一個路由組件。對於前面提到的,在不設置 exact
屬性的前提下,/home
會同時匹配 /
和 /home
兩個路由組件,使用 Switch
能夠進行單一匹配,但與放置順序也有關。
<Switch> <Route path="/home" component={ Home } /> <Route path="/" component={ Dashboard } /> </Switch>
封裝了 <a>
標籤的組件進行路由跳轉。
<Link to="/home">to home</Link>
與 Link
的用法相似,會默認給當前路由路徑與 to
屬性匹配的組件添加 active
類名。
<NavLink to="/home">to home</NavLink> <NavLink exact to="/other">to other</NavLink> <NavLink to="/other/1">to other/1</NavLink>
當點擊 to other/1
連接時,to other
連接也會被添加上 active
類名,這與 Router
組件是相似的,因此對於這樣的導航,一般須要添加 exact
屬性。
to
屬性進行重定向,一般會用在 Switch
中,做爲匹配失敗的處理。
useHistory()
返回的 history
對象,調用 push
方法。
// 路由組件 <Route path="/home/:id" component={ Home }/> // Home.tsx interface RouteParams { id: string } export default () => { const params = useParams<RouteParams>(); return ( <div> { params.id } </div> ) } // Link 跳轉 <Link to="/home/1">to home</Link> // history 跳轉 import { useHistory } from 'react-router-dom'; export default () => { const history = useHistory(); const pushRouteParams = () => { history.push('/home/1') }; return ( <div> <Button onClick={ pushRouteParams }>to /home/1</Button> </div> ); };
// 路由組件 <Route path="/home" component={ Home }/> // Home.tsx import { useLocation } from 'react-router-dom'; export default () => { const location = useLocation(); return ( <div> { location.state && location.state.id } </div> ) } // Link 跳轉 <Link to={{ pathname: '/home', state: { id: 1 } }}>to home</Link> // history 跳轉 history.push({ pathname: '/home', state: { id: 1 } })
// @types/history export interface Location<S = LocationState> { pathname: Pathname; search: Search; state: S; hash: Hash; key?: LocationKey; }
location
對象沒有 query
屬性了,應該是不提供這個方法了吧……
// Link <Link to="/home" /> <Link to="/home" replace/> // history history.push(...) history.replace(...)
v4 以後再也不提供 onEnter
、onUpdate
和onLeave
等函數,而是在路由組件中分別對應 React 中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
等生命週期方法,這剛好就可使用新特性 useEffect
進行替換了。
在路由組件內部使用 useEffect
,配合 history.replace()
進行路由權限控制
const history = useHistory(); const state = true; useEffect(() => { if (!state) { history.replace('/'); } });
也寫成一個自定義 Hook
,在多個路由組件中使用
function useUserRole(state: boolean) { const history = useHistory(); useEffect(() => { if (!state) { history.replace('/'); } }); } useUserRole(false);