在react中,一般都是使用單頁面應用(SPA),即整個頁面只有一個html,而後經過不一樣的url地址進行組件的匹配和切換。html
咱們看到的url地址可能會有兩種形式,一種是 localhost:3000/home,一種是 localhost:3000/#/home,兩種地址的區別在於有無#,有#的是根據hash來進行匹配,即url中的錨點,本質上是經過location.hash來改變href,hash後的內容是不會發送給服務器的,沒有#是經過html5的history來進行跳轉,二者跳轉後都不會進行刷新。html5
如下路由是美團和網易雲音樂的,能夠看到他們的路由地址的分別是基於hash和history
react
具體來講,經過#來區分路由的實現原理是監聽hash的變化,這裏監聽的方式是使用hashchange 事件,當url地址發生了變化時,經過匹配當前url的地址來進行展現,代碼以下所示web
<body> <div> <a href="#/home">首頁</a> <a href="#/about">關於</a> </div> <div id="content"></div> <script> window.addEventListener('hashchange',()=>{ const content = document.getElementById('content') switch(location.hash){ case '#/home': content.innerHTML = '首頁' break case '#/about': content.innerHTML = '關於' break default: content.innerHTML = '' } }) </script> </body>
經過html5的history來實現跳轉無刷新,就須要阻止a標籤的默認行爲,再經過history的pushState這一方法實現url地址的替換,一樣是監聽url地址的變化,這裏使用popState方法,當url地址發生了變化以後,展現對應的內容。服務器
<body> <div> <div> <a href="/home">首頁</a> <a href="/about">關於</a> </div> <div id="content"> </div> </body> <script> const content = document.getElementById('content') const aEles = document.getElementsByTagName('a') for (let el of aEles) { el.addEventListener('click', (event) => { event.preventDefault() const href = el.getAttribute("href") history.pushState({}, '', href) urlchange() }) } window.addEventListener('popstate', () => { urlchange() }) function urlchange() { switch (location.pathname) { case '/home': content.innerHTML = '首頁' break case '/about': content.innerHTML = '關於' break default: content.innerHTML = '' } } </script>
以上的錨點和history分別對應了react-router中的HashRouter和BrowserRouter,在react-router中,想要使用路由組件必須在最外層包裹一層HashRouter或者BrowserRouter,來選擇想要使用的路由類型,而後才能使用react-router提供的其它組件,react-route中用於web端的庫爲react-router-dom,如下全部的組件都是從react-router-dom中導出。react-router
下面來講說react-router的經常使用組件dom
一、<Link>和<NavLink>,這兩個標籤都是由a標籤的封裝,經過to屬性指定跳轉的連接地址,<NavLink>比<Link>多的是能夠指定選中的樣式和類名,格式如this
<Link to="/about">關於</Link>
經過<Link>標籤,點擊以後就能夠跳轉到指定的地址,此時即便外層包裹的是HashRouter,也不須要本身加上#,react-router會幫咱們在url上加上#
url
二、當使用<Link>定義了跳轉的url地址後,此時須要指定匹配跳轉該url地址時須要顯示的內容,此時使用的是<Route>,經過path屬性指定url地址,component屬性指定渲染的組件,格式如spa
<Route path="/about" component={About} />
加上了以後, /about這個地址顯示的就是About這個組件裏的內容。<Route>進行的是模糊匹配,一個url路徑可能能夠匹配多個Route,若是須要嚴格匹配的話,能夠增長一個屬性 exact,適合沒有二級路由的時候開啓。
三、在有不少的Route的狀況下,即便在第一個Route匹配到合適的以後,仍然會繼續向下匹配,直到最後一個,因此在全部的<Route>外包裹一個<Switch>標籤可讓它進行惟一的匹配,匹配到合適的以後就不繼續匹配了。
四、當全部的<Route>都沒法匹配到url上的地址時,能夠定義 <Redirect>組件直接重定向到一個頁面,經過to來指定路由地址,這個組件要放置到<Route>的最後面,由於它和<Link>不一樣,<Link>是點擊了以後纔會跳轉對應的地址,而<Redirect>會直接執行並跳轉
<Redirect to="/about"/>
五、經過路由來匹配的組件稱爲路由組件,<Route path="/about" component={About} />,這裏的About就是路由組件,和其它的組件是不同的,路由組件的props裏有一些數據,其中包括三大屬性,history、location和match,history能夠自定義頁面的跳轉,location用來獲取url地址相關的信息,match能夠用做動態路由的匹配。
但通常的組件是沒有這些props屬性的,若是通常的組件也須要這樣一些屬性的話,能夠經過一個高階組件 withRouter。如
class myCom extends PureComponent { } export default withRouter(myCom)
再來講說路由傳參
有時候,咱們但願在連接上帶一個id值或者兩個頁面之間跳轉的時候傳遞一些參數,這時候有三種路由傳參方式
一、params傳參
<NavLink to="/detail/1">商品詳情</NavLink> <Route path="/detail/:id" component={Detail}/> // 若是是自行定義跳轉的地址能夠經過 this.props.history.push("/detail/1")
此時的id就是動態的,能夠在id這個位置傳遞任意的數值或者字符串,而後經過props裏的match對象中的params獲取動態匹配的內容
二、search傳參
<NavLink to="/detail?id=1">商品詳情</NavLink> <Route path="/detail" component={Detail}/> // 若是是自行定義跳轉的地址能夠經過 this.props.history.push("/detail?id=1")
此時至關於在url上添加一個問號進行拼接,須要把地址拼成一種鍵值對的形式,經過 props裏的location對象中的search屬性獲取從問號開始的匹配內容,這一種路由的匹配方式須要自行解析字符串
三、state傳參
<NavLink to={ pathname: "/detail", state: { id: 1} }>商品詳情</NavLink> <Route path="/detail" component={Detail}/> // 若是是自行定義跳轉的地址能夠經過 this.props.history.push("/detail", { id: 1})
這樣的傳遞參數方式經過props裏的location對象中的state屬性來獲取傳遞的值,這種方式的能夠直接以對象的形式傳遞,而且可傳遞的數據更多,這些參數不會顯示在url上
用一個小的組合案例展現以上內容
import React, { PureComponent } from 'react' import { NavLink, Route, Switch, withRouter, Redirect } from "react-router-dom" import Home from "./pages/Home" import About from "./pages/About" import Detail from "./pages/Detail" import Product from './pages/Product' class App extends PureComponent { jumpToProduct(){ this.props.history.push('/product') } render() { return ( <div> <NavLink exact to="/">首頁</NavLink> <NavLink to="/about">關於</NavLink> <NavLink to="/detail/1">詳情</NavLink> <button onClick={e=>this.jumpToProduct()}>商品</button> <Switch> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/detail/:id" component={Detail} /> <Route path="/product" component={Product}/> <Redirect to="/"/> </Switch> </div> ); } } export default withRouter(App)