HashRouter
HashRouter只是一個容器, 并沒有DOm 結構,它渲染的就是它的子組件,并向下層傳遞location, 當hash值發(fā)生變化的時候會通過hashchange捕獲變化,并給pathname重新賦值
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter, Route, Switch, Link, Redirect,withRouter } from 'react-router-dom'
//import { HashRouter, Route, Link, Switch, Redirect } from './react-router-dom'
import App from './App';
import Home from './Home'
import User from './User'
import NavHeader from './NavHeader'
import './index.css';
ReactDOM.render(
<HashRouter>
<NavHeader title="返回首頁"></NavHeader>
<Link to='/home'> Home</Link>
<Link to='/user'> User</Link>
<Link to='/app'> App</Link>
<Switch>
<Route path="/app" component={App} />
<Route path="/home" component={Home} />
<Route path="/home/123" component={Home} />
<Route path="/user" component={User} />
<Redirect to='/app' />
</Switch>
</HashRouter>,
document.getElementById('root')
);
HashRouter的實現(xiàn)如下
import React, { PureComponent } from 'react'
import { Provider } from './Context'
export default class HashRouter extends PureComponent {
constructor(props) {
super(props)
this.state = {
location: {
pathname: window.location.hash.slice(1) || '',
state: window.history.state
}
}
}
componentDidMount() {
//如果沒有hash值就給一個默認值
window.location.hash = window.location.hash || '/'
window.addEventListener('hashchange', () => {
this.setState({
...this.state,
location: {
...this.state.location,
pathname: window.location.hash.slice(1)
}
})
})
}
componentWillUnmount() {
window.removeEventListener('hashchange')
}
render() {
const value = {
location: this.state.location,
history: {
push(path) {
window.location.hash = path
}
}
}
return (
<Provider value={value}>
{this.props.children}
</Provider>
)
}
}
因為HashRouter渲染的是它的子組件种樱,那么子組件里面有可能嵌套著二級三級路由舍咖,這個時候就需要上下文Context來讀取嵌套的值蚓庭,需要創(chuàng)建一個Context
import React from 'react'
const { Provider, Consumer } = React.createContext()
export { Provider, Consumer }
Route
route代表一條路由規(guī)則娇跟,path代表此規(guī)則的路徑言津, component代表要渲染的組件,如果說通過Context傳下來的路徑location.pathname與當前屬性中的路徑path相匹配就進行渲染
import React, { PureComponent } from 'react'
import { pathToRegexp } from 'path-to-regexp'
import { Consumer } from './Context'
export default class Route extends PureComponent {
render() {
const { path, component: Component, exact = false } = this.props
return (
<Consumer>
{state => {
const { pathname } = state.location
const regexp = pathToRegexp(pathname, [], { end: exact });
if (regexp.test(path)) {
return <Component />
}
return null
}}
</Consumer>
)
}
}
Link超鏈接
點擊某個鏈接跳轉到指定頁面寓免,它的渲染結構就是一個a鏈接胸梆,href就是屬性to對應的值,所以可以這么實現(xiàn)link方法:
import React from 'react'
import { Consumer } from './Context'
export default function Link(props) {
return (
<Consumer>
{
state => (
// eslint-disable-next-line no-template-curly-in-string
<a href="{`#${props.to}`}" onClick={(e) => {
// 阻止默認行為
e.preventDefault()
state.history.push(props.to)
}}>{props.children}</a>
)
}
</Consumer>
)
}
Switch
switch是為了解決route的唯一渲染扫责,保證路由只渲染一個路徑榛鼎。
import React from 'react'
import { Consumer } from './Context'
import { pathToRegexp } from 'path-to-regexp'
export default function (props) {
return (
<Consumer>
{value => {
let children = props.children
const { pathname } = value.location
//判斷是否是數(shù)組,如果不是就包裝成數(shù)組
children = Array.isArray(children) ? children : [children]
for (let i = 0; i < children.length; i++) {
//child是一個react元素它的返回值是一個虛擬dom {type:Route,props:{exact,path,component}}
let child = children[i]
let { path = '/', exact = false } = child.props
let regexp = pathToRegexp(path, [], { end: exact })
let matched = pathname.match(regexp)
//若匹配進行渲染
if (matched) {
return child
}
}
//若不匹配就返回null
return null
}}
</Consumer>
)
}
Redirect
重定向鳖孤,當所有都不匹配的時候會重定向到新的頁面者娱,就是改變path值驅動頁面重新渲染。
import React from 'react'
import { Consumer } from './Context'
export default function (props) {
return (
<Consumer>
{
value => {
//當Redirect元素的props.from屬性和當前l(fā)ocation.pathname屬性相等時或者from屬性不存在時就直接跳轉到to
if (!props.from || props.from === value.location.pathname) {
value.history.push(props.to)
}
return null
}
}
</Consumer>
)
}
withRouter
withRouter是一個高階組件苏揣,它的作用是將一個自定義組件包裹進Route里面, 然后react-router的三個對象history, location, match就會被放進這個組件的props屬性中黄鳍。從而實現(xiàn)自定義組件的路由跳轉
查看 Navheader 組件, 用 withRouter 包裹后 可以自定義路由跳轉
import React from 'react'
import { withRouter } from './react-router-dom'
function Navheader(props) {
return (
<div className="navbar-heading">
<div
onClick={() => props.history.push('/user')} //點擊的時候跳轉到首頁
className="navbar-brand">{props.title}</div>
</div>
)
}
export default withRouter(Navheader)
withRouter 的實現(xiàn):
import React from 'react'
import { Consumer } from './Context'
export default function (OldComponent) {
function routerWrapper(props) {
return <Consumer>
{
value => <OldComponent {...props} {...value} />
}
</Consumer>
}
return routerWrapper;
}
代碼地址 React路由實現(xiàn)