React Router 是完整的 React 路由解決方案。
React Router 保持 UI 與 URL 同步。它擁有簡單的 API 與強大的功能例如代碼緩衝加載、動態路由匹配、以及建立正確的位置過渡處理。你第一個念頭想到的應該是 URL,而不是事後再想起。
路由的環境搭建和配置
使用一下命令安裝 React
的路由。
react-router
: 實現了路由的核心功能
react-router-dom
: 基於 react-router
,加入了在瀏覽器運行環境下的一些功能,例如:Link
組件,會渲染一個 a
標籤,Link
組件源碼 a
標籤行; BrowserRouter
和 HashRouter
組件,前者使用pushState
和 popState
事件構建路由,後者使用window.location.hash
和 hashchange
事件構建路由。
// react-router-dom 依賴於 react-router,安裝它時會自動安裝 react-router
npm i react-router-dom --save
路由的調用、渲染和封裝
react-router
和 vue-router
基本原理比較相似,都是通過控制網頁 url
變化實現前端路由,在不刷新頁面的基礎上更新頁面的內容,或者跳轉頁面。
一、React-router
配置和調用的簡單示例
// App.js
import React from 'react';
// 從 react-router-dom 中導入配置路由需要的組件
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// 導入被路由的子組件
import About from './components/About'
import Topics from './components/Topics'
import './App.css';
// 配置路由並調用路由
function App() {
return (
<div className="App">
<div>其他內容區域</div>
<Router>
<div>
<ul>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<hr />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
</div>
);
}
export default App;
注: 如果一個組件中需要配置或使用路由,建議將 BrowserRouter
作爲其根組件。
在項目研發過程中,一般一個項目的根組件就是根路由,其上所有的路由都是在根路由的基礎上衍生的子路由,由於react-router
本身的特點比較特殊,我們會將 <App>
組件渲染在根路由配置中,然後將 index.js
邏輯入口中的根組件渲染成根路由組件,以下是示例:
// 根路由配置文件 router.js
import React, { Component } from 'react';
import { BrowserRouter as Router} from 'react-router-dom';
import App from './App';
class Router extends Component {
render() {
return (
<Router>
<App />
</Router>
);
}
}
export default Router;
// 邏輯入口 index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Router from './router';
// 用 Router 組件代替 App 作爲根組件
ReactDOM.render(<Router />, document.getElementById('root'));
嵌套路由和默認路由
一、 嵌套路由
嵌套路由是在當前被路由組件內部再配置路由並且在對應位置渲染即可。
比如在上面的示例中,<App>
組件中有兩個被路由組件,分別是 <About />
和 <Topics />
,這相當於一級路由,如果 <About />
組件中還有子路由 <A1 />
組件和 <A2 />
組件,那麼可以進行如下配置:
// About.js
import React from 'react'
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import A1 from './A-1'
import A2 from './A-2'
class About extends React.Component {
render() {
return (
<Router>
<ul>
<li>
<Link to="/about/a1">a-module1</Link>
</li>
<li>
<Link to="/about/a2">a-module1</Link>
</li>
</ul>
<Route path="/about/a1" component={A1} />
<Route path="/about/a2" component={A2} />
</Router>
);
}
}
export default About;
二、默認路由
在上面的示例中,如果在菜單切換到 <About />
組件時,需要展示 <A1 />
組件,就要設置默認路由:
react-router
可以通過兩種方式實現路由:
方式一
{/* 當路由切換到 /about 時,會自動渲染 A1 組件 */}
<Route path="/about" exact component={A1}></Route>
<Route path="/about/a1" component={A1} />
<Route path="/about/a2" component={A2} />
方式二
{/* 當路由切換到 /about 時,會自動重定向到 /about/a1 */}
<Redirect path="/about" to="/about/a1" />
<Route path="/about/a1" component={A1} />
<Route path="/about/a2" component={A2} />
<Redirect />
組件會重定向 url
。
react-router
4.x 版本推出之後,路由配置不再有集中的配置。
路由參數的傳遞和接受
路由傳參時單頁面應用開發常見的需求。
一、在 url
中使用通配符直接傳參
給一個需求,有一個博客列表菜單,點任意一篇,要在路由位置渲染對應的數據。
那麼我們就需要在點擊菜單時渲染路由組件並傳參:
// Blog-list.js
import React from 'react'
import { BrowserRouter as Router, Route, Link, Redirect } from "react-router-dom";
import Blog from './Blog'
class BlogList extends React.Component {
render() {
return (
<Router>
<ul>
<li>
<Link to="/blog/1">博客1</Link>
</li>
<li>
<Link to="/blog/2">博客2</Link>
</li>
<li>
<Link to="/blog/3">博客3</Link>
</li>
</ul>
<Route path="/blog/:id" component={Blog} />
</Router>
);
}
}
export default BlogList
在 <Blog />
組件中獲取參數
// Blog.js
import React from 'react'
class A1 extends React.Component {
render() {
// 接受參數列表
const match = this.props.match;
return <h1>
{match.params.id}
</h1>;
}
}
export default A1
這種調用和傳參會在 url
中暴露參數。
二、使用 query
傳參
<Link to={
{
pathname: '/about/a1',
query: {
id: 1,
name: 'tom'
}
}
}>詳情頁</Link>
接收參數
import React from 'react'
class Detail extends React.Component {
render() {
// 接受 query 的參數
const query = this.props.location.query;
console.log(query);
return <h1>詳情頁</h1>;
}
}
export default Detail;
注:
- 通過
query
傳參不會將參數暴露在url
中,但是在刷新當前頁面時,參數會丟失。 - 必須由其他頁面跳轉過來,參數纔會被傳遞。
- 使用
query
傳參是不需要配置路由表的。
三、使用 state
傳值
<Link to={
{
pathname: '/about/a1',
state: {
id: 1,
name: 'tom'
}
}
}>詳情頁</Link>
接收參數
import React from 'react'
class Detail extends React.Component {
render() {
// 接受 state 的參數
const state = this.props.location.state;
console.log(state);
return <h1>詳情頁</h1>;
}
}
export default Detail;
注:
- 和
query
一樣state
傳參時,參數也是加密的。
react-router
爲state
傳值提供了各種動態操作的方式。
四、使用 search
傳參
<Link to={
{
pathname: '/about/a1',
search: '?id=1&name=tom'
}
}>詳情頁</Link>
路由中的 <Switch>
react-router
提供 <Switch>
組件用來根據條件,選擇性渲染列表中的一個路由。
給一個需求,用戶如果對 url
做一些違規操作,比如輸入一個不存在的路由時,展示 404 組件,那麼代碼可能是這樣
<Route path="/about" component={About}/>
<Route path="/user" component={User}/>
{/* 404 組件 */}
<Route component={NoMatch}/>
這種寫法,當路由匹配到 '/about'
和 'user'
時,<About>
組件和 <User>
會和 404 組件同時渲染。
但實際需求它們是互斥的,這個時候就要用到 <Switch>
組件。
渲染第一個被location匹配到的並且作爲子元素的 <Route>
或者 <Redirect>
{/* Switch 和 Link、Route 一樣,在使用時需要先從 react-router-dom 中引入 */}
<Switch>
<Route path="/about" component={About}/>
<Route path="/user" component={User}/>
{/* 404 組件 */}
<Route component={NoMatch}/>
</Switch>
<HashRouter>
和 <BrowserRouter>
如果一個頁面有這樣的基本操作,當前端訪問 '/'
時,渲染 <Home>
組件,當訪問 '/about'
時,渲染 <About>
組件。
先看路徑區別:
BorserRouter:
http://www.xxx.com/about
HashRouter:
http://www.xxx.com/#/about
如果使用 <BrowserRouter>
,每次前端做出請求,服務器都要對請求做出分析,並且返回該請求對應的內容給前端,前端渲染即可。
如果使用 <HashRouter>
,'#'
後面的內容不會傳到服務器,無論路由如何,對於服務器端來說,訪問的都是 '/'
,每次請求,服務器都返回同樣的內容給瀏覽器,由瀏覽器自己解析 '#'
之後的內容,渲染對應的內容。
動態路由
react-router
之所以推出 v4 版本,就是因爲覺得靜態路由還有很多不完善的地方。
react-router
v4 允許使用各種條件判斷是否需要渲染某個<Route>
組件。
比如某個組件需要用戶滿足權限才能渲染:
<Switch>
<Route exact path='/' component={Home}/>
{
userState == 4 &&
<Route exact path='/product' component={Product}/>,
}
<Route path='/about' component={About}/>
</Switch>