Reaact 路由之 HashRoute 實現(xiàn)

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)

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市平匈,隨后出現(xiàn)的幾起案子框沟,更是在濱河造成了極大的恐慌,老刑警劉巖增炭,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忍燥,死亡現(xiàn)場離奇詭異,居然都是意外死亡隙姿,警方通過查閱死者的電腦和手機灾前,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孟辑,“玉大人哎甲,你說我怎么就攤上這事∷撬裕” “怎么了炭玫?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長貌虾。 經(jīng)常有香客問我吞加,道長,這世上最難降的妖魔是什么尽狠? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任衔憨,我火速辦了婚禮,結果婚禮上袄膏,老公的妹妹穿的比我還像新娘践图。我一直安慰自己,他們只是感情好沉馆,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布码党。 她就那樣靜靜地躺著德崭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪揖盘。 梳的紋絲不亂的頭發(fā)上眉厨,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音兽狭,去河邊找鬼憾股。 笑死,一個胖子當著我的面吹牛箕慧,可吹牛的內(nèi)容都是我干的荔燎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼销钝,長吁一口氣:“原來是場噩夢啊……” “哼有咨!你這毒婦竟也來了?” 一聲冷哼從身側響起蒸健,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤座享,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后似忧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渣叛,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年盯捌,在試婚紗的時候發(fā)現(xiàn)自己被綠了淳衙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡饺著,死狀恐怖箫攀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幼衰,我是刑警寧澤靴跛,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站渡嚣,受9級特大地震影響梢睛,放射性物質發(fā)生泄漏。R本人自食惡果不足惜识椰,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一绝葡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腹鹉,春花似錦藏畅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至航瞭,卻和暖如春诫硕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刊侯。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工章办, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滨彻。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓藕届,卻偏偏與公主長得像,于是被迫代替她去往敵國和親亭饵。 傳聞我的和親對象是個殘疾皇子休偶,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355