認知
之前沒有前後端分離的時候,路由幾乎都是針對後臺而言的,有人說掌控了路由就相當於佔了主權地位,我覺得這句話是不為過的。因為路由才能決定你的頁面從哪裡來,到哪裡去。現在的前後端分離專案,路由幾乎都給了前端處理,比如你經常使用的vue-router,react-router,今天就react路由為基礎,一起實現下React開發中常常使用的路由那些個東東。
選擇
react-router 還是 react-router-dom? 在 React 的使用中,我們一般要引入兩個包,react 和 react-dom,那麼 react-router 和react-router-dom 是不是兩個都要引用呢?答案是它們兩個只要引用一個就行了,兩者區別是後者比前者多出了 <Link> <BrowserRouter> 這樣的 DOM 類元件。 因此我們只需引用 react-router-dom 這個包就行了。當然,如果搭配 redux ,你還需要使用 react-router-redux。what's the diff betweenreact-router-dom
& react-router
? #4648
HashRouter
本文依舊運用測試驅動開發的模式進行理順思路。。。。。。Begin。。。。
- 根據不同的路由渲染三個不同的元件(Home,News,About)
let Home = () => <div>首頁</div>;
let News = () => <div>新聞</div>;
let About = () => <div>關於我們</div>;
複製程式碼
渲染方法:
ReactDOM.render(
<Router>
<Route path='/home' component={Home}></Route>
<Route path='/news' component={News}></Route>
<Route path='/about component={About}></Route>
</Router>,
document.querySelector('#app')
)
複製程式碼
通過以下傳遞引數的方法,觀察得到有用的引數(圖1)
let Home = (props,context)=> {
console.log(props)
return <div>首頁</div>;
}
複製程式碼
提取最主要的引數:
{
history:{
push()
},
location:{pathname:'/home'},
match{
params:{},
path:'/home',
url:'/home'
}
}
複製程式碼
編寫父元件HashRouter.js,以及單條路由Route.js
HashRouter.js:
在元件掛載的時候,監聽hashchange事件,即重新渲染頁面
componentWillMount(){
window.location.hash = window.location.hash || '/';
let render = ()=>{
this.setState({});
}
window.addEventListener('hashchange',render);
}
複製程式碼
通過上下文context進行父子元件之間的通訊,傳遞location.pathname值
static childContextTypes = {
location:PropTypes.object
}
constructor(props){
super(props);
this.state = {};
}
getChildContext(){
return {
location:{pathname:window.location.hash.slice(1)||'/'}
}
}
複製程式碼
HashRouter具體render的物件為子元件,本身並沒有東西需要render。
render(){
return this.props.children;
}
複製程式碼
Route.js:
獲取context中的pathname,跟元件傳進來的path進行比較,看是否相等,相等則渲染傳遞進來的props中的component
static contextTypes = {
location: PropTypes.object
}
render(){
let {path,component:Component} = this.props;
let {location:{pathname}} = this.context;
if(path == pathname || pathname.startsWith(path)){
return <Component location={this.context.location} history={this.context.history}/>;
}
return null;
}
複製程式碼
到此通過瀏覽器改變輸入的hash值,就可以切換到不同的元件,顯示不同的內容。
2. 通過導航的形式,點選頁面的導航條,切換到不同的頁面(Link元件)
本質為a標籤。點選通過上下文的history改變hash。
HashRouter:
static childContextTypes = {
location:PropTypes.object
}
getChildContext(){
return {
location:{pathname:window.location.hash.slice(1)||'/'},
history:{
push(path){
window.location.hash = path;
}
}
}
}
複製程式碼
Link.js
import React,{Component} from 'react';
import ProTypes from 'prop-types';
export default class Link extends Component{
static contextTypes = {
history:ProTypes.object
}
render(){
return (
// <a href={"#"+this.props.to}>{this.props.children}</a>
<a onClick={()=>this.context.history.push(this.props.to)}>{this.props.children}</a>
)
}
}
複製程式碼
呼叫:
<ul className='nav navbar-nav'>
<li>
<Link to='/home'>首頁</Link>
</li>
<li>
<Link to='/news'>新聞管理</Link>
</li>
<li>
<Link to='/about'>關於我們</Link>
</li>
</ul>
複製程式碼
-
二級路由
我們建立一個新聞管理的類News.js,在這裡進行二級路由的分發,有一個新聞列表(路由為/news/list,元件為NewsList),和一個新增新聞(路由為/news/add,元件為NewsAdd),點選新增新聞和新聞列表 可以跳轉到相對應的路由 此項功能通過之前的實現是支援的,無須對我們自己的HashRouter等進行改寫 -
路徑引數實現之params
express中,vue中都有類似於'/news/datail/:id'這樣的路徑,後面的id是動態匹配的,即為路徑引數。而且相似的是這些實現都用到了path-to-regxp這個庫。這裡我們也重點使用這個庫實現路徑引數的功能。 在News.js中新增一條路由資訊,可以跳轉到NewsDetail詳情頁。
<Route path='/news/datail/:id' component={NewsDetail}></Route>
複製程式碼
然後在Route.js新增constructor,通過path-to-regexp獲取到正則匹配路徑資訊,並且修改render中路徑匹配的方法如下:
constructor(props) {
super(props);
let { path } = props;
this.keys = [];
this.regxp = pathToRegexp(path, this.keys, { end: false });
this.keys = this.keys.map(key => key.name);
}
let { location } = this.context;
let result = location.pathname.match(this.regxp);
let props = {
location,
history: this.context.history
}
if (result) {
let [url, ...values] = result;
props.match = {
url,
path,
params: this.keys.reduce((memo, key, idx) => {
memo[key] = values[idx];
return memo;
}, {})
}
return <Component {...props}></Component>
} else {
return null;
}
複製程式碼
上述props.match是很重要的一部,拿到match的資訊 圖1中也有顯示
- Switch元件 有的時候在我們寫路由資訊的時候,會手誤寫成兩個,比如
<Route path='/home' component={Home}></Route>
<Route path='/home' component={Home}></Route>
<Route path='/news' component={News}></Route>
<Route path='/about' component={About}></Route>
複製程式碼
這裡有兩個/home,那麼頁面就會顯示兩次,這時候我們需要些一個Switch元件,套在最外層,那麼原理就是依次匹配,匹配到了直接返回,不再往下匹配。由此得出Switch.js的邏輯:
<Switch>
<Route path='/home' component={Home}></Route>
<Route path='/home' component={Home}></Route>
<Route path='/news' component={News}></Route>
<Route path='/about' component={About}></Route>
</Switch>
export default class Switch extends Component {
static contextTypes = {
location: ProTypes.object
}
render() {
let { pathname } = this.context.location;
let children = this.props.children;
for (let i = 0; i < children.length; i++) {
let child = children[i];
let { path } = child.props;
if (pathToRegexp(path, [], { end: false }).test(pathname)) {
return child;
}
}
return null;
}
}
複製程式碼
小結
未完待續。。。。