摘要:要實(shí)現(xiàn)的功能我們使用開發(fā)項(xiàng)目的時(shí)候,基本上是單頁(yè)面應(yīng)用,也就離不開路由。路由看似神秘,當(dāng)我們簡(jiǎn)單的模擬一下它的核心功能后,發(fā)現(xiàn)也就這么回事兒。實(shí)現(xiàn)的邏輯是,返回中跟匹配到的第一個(gè)孩子。
1 要實(shí)現(xiàn)的功能
我們使用React開發(fā)項(xiàng)目的時(shí)候,基本上是單頁(yè)面應(yīng)用,也就離不開路由。路由看似神秘,當(dāng)我們簡(jiǎn)單的模擬一下它的核心功能后,發(fā)現(xiàn)也就這么回事兒。本文就詳細(xì)的介紹一下react-router-dom 的 HashRouter的核心實(shí)現(xiàn)邏輯。
本文實(shí)現(xiàn)的功能主要包含:
HashRouter
Route
Link
MenuLink
Switch
Redirect
2 實(shí)現(xiàn)的邏輯先不說(shuō)代碼是怎樣寫的,先上圖,讓大家看一下這個(gè)HashRouter到底是個(gè)什么東東:
好吧,肯定有人會(huì)說(shuō)這些圈圈又是什么東東呀,客官不要著急,待我慢慢解釋:
HashRouter是一個(gè)大的容器,它控制著他自己到底渲染成什么樣子,那么它是通過(guò)什么控制的呢,看它的名字就能猜出來(lái),那就是window.location.hash。
當(dāng)HashRouter開始渲染的時(shí)候就會(huì)拿它自己身上的pathname屬性跟它肚子里的Route的path進(jìn)行匹配,匹配上的話,就會(huì)渲染Route的component對(duì)應(yīng)的組件。
Link是怎樣切換路由的呢,很簡(jiǎn)單,就是通過(guò)this.props.history.push(path)來(lái)改變HashRouter中的pathname屬性,進(jìn)而驅(qū)動(dòng)Route們 進(jìn)行重新渲染,再次匹配我們的路由,最終實(shí)現(xiàn)路由的切換。
介紹了一下簡(jiǎn)單的邏輯,接下來(lái)我們就看一下具體是怎樣實(shí)現(xiàn)的吧,如下圖:
HashRouter是一個(gè)繼承了React.Component的類,這個(gè)類上的state包括location,監(jiān)聽著hash的變化以驅(qū)動(dòng)Route組件的重新渲染,另外還有一個(gè)history屬性,可以切換頁(yè)面的路由。
本文要實(shí)現(xiàn)的功能中包括Route、Link、MenuLink、Switch、 Redirect,其中Route的是基礎(chǔ)是核心,MenuLink和某些有特定邏輯的渲染都是在Route的基礎(chǔ)上實(shí)現(xiàn)的。
Route組件上可以接收三種變量,包括component、render、children,其中render、children是都是函數(shù),render是根據(jù)特定的邏輯渲染元素,children是用來(lái)渲染MenuLink,這兩個(gè)函數(shù)都接收當(dāng)前路由的props,函數(shù)的返回值是要渲染的元素。
Switch實(shí)現(xiàn)的邏輯是,返回children中跟hash匹配到的第一個(gè)“孩子”。
3 具體的代碼邏輯 (1) HashRouterHashRouter將window.loacation.hash跟自己的state掛鉤,通過(guò)改變自己的state驅(qū)動(dòng)頁(yè)面的重新渲染。
import React, {Component} from "react"; import PropTypes from "prop-types"; export default class HashRouter extends Component { constructor() { super(); this.state = { location: { pathname: window.location.hash.slice(1) || "/", // 當(dāng)前頁(yè)面的hash值 state: {} //保存的狀態(tài) } }; } // 定義上下文的變量類型 static childContextTypes = { location: PropTypes.object, history: PropTypes.object } // 定義上下文的變量 getChildContext() { return { location: this.state.location, history: { push: (path) => { // 就是更新 window.hash值 if (typeof path === "object") { let {pathname, state} = path; this.setState({ location: { ...this.state.location, state // {from: "/profile"} } }, () => { window.location.hash = pathname; }) } else { window.location.hash = path; } } } } } render() { return this.props.children; // 渲染頁(yè)面元素 } componentDidMount() { window.location.hash = window.location.hash.slice(1) || "/"; // 監(jiān)聽window的hash的變化,驅(qū)動(dòng)頁(yè)面的重新刷新 window.addEventListener("hashchange", () => { this.setState({ location: { ...this.state.location, pathname: window.location.hash.slice(1) || "/" } }); }) } }(2) Route
Route的渲染核心邏輯就是將自己的path和當(dāng)前頁(yè)面的hash進(jìn)行匹配,匹配上了就渲染相應(yīng)的元素,匹配不上就什么都不渲染。
import React, {Component} from "react"; import PropTypes from "prop-types"; import pathToRegexp from "path-to-regexp" export default class Route extends Component { // 定義上下文context的類型 static contextTypes = { location: PropTypes.object, history: PropTypes.object } render() { // 解構(gòu)傳入Route的props let {path, component: Component, render, children} = this.props; // 解構(gòu)上下文的屬性 let {location, history} = this.context; let props = { location, history }; // 將傳入Route的path和當(dāng)前的hash進(jìn)行匹配 let keys = []; let regexp = pathToRegexp(path, keys, {end: false}); keys = keys.map(key => key.name); let result = location.pathname.match(regexp); if (result) { // 匹配上了 let [url, ...values] = result; props.match = { path, url, params: keys.reduce((memo, key, index) => { // 獲取匹配到的參數(shù) memo[key] = values[index]; return memo; }, {}) }; if (Component) { // 普通的Route return(3) Redirect; } else if (render) { // 特定邏輯的渲染 return render(props); } else if (children) { // MenuLink的渲染 return children(props); } else { return null; } } else { // 沒有匹配上 if (children) { // MenuLink的渲染 return children(props); } else { return null; } } } }
Redirect就干了一件事,就是改變HashRouter的state,驅(qū)動(dòng)重新渲染。
import React, {Component} from "react"; import PropTypes from "prop-types"; export default class Redirect extends Component { // 定義上下文context的Type static contextTypes = { history: PropTypes.object } componentDidMount() { // 跳轉(zhuǎn)到目標(biāo)路由 this.context.history.push(this.props.to); } render() { return null; } }(4) MenuLink
import React, {Component} from "react"; import Route from "./Route"; import Link from "./Link" export default ({to, children}) => { // 如果匹配到了,就給當(dāng)前組件一個(gè)激活狀態(tài)的className return(5) Link( {children} ) }/> }
Link就是渲染成一個(gè)a標(biāo)簽,然后給一個(gè)點(diǎn)擊事件,點(diǎn)擊的時(shí)候更改HashRouter的狀態(tài),驅(qū)動(dòng)重新渲染。
import React, {Component} from "react"; import PropTypes from "prop-types"; export default class Link extends Component { static contextTypes = { history: PropTypes.object } render() { return ( this.context.history.push(this.props.to)}>{this.props.children} ) } }(6) Switch
import React, {Component} from "react"; import PropTypes from "prop-types"; import pathToRegexp from "path-to-regexp"; export default class Switch extends Component { static contextTypes = { location: PropTypes.object } render() { let {pathname} = this.context.location; let children = this.props.children; for (let i = 0, l = children.length; i < l; i++) { let child = children[i]; let path = child.props.path; if (pathToRegexp(path, [], {end: false}).test(pathname)) { // 將匹配到的第一個(gè)元素返回 return child; } } return null } }4 寫在最后
好了,這幾個(gè)功能介紹完了,你是否對(duì)HashRouter的原理有所了解了呢?本文只是貼出部分代碼,如果有需要請(qǐng)看demo可以手動(dòng)體驗(yàn)一下哦。
參考文獻(xiàn):
react-router
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94153.html
摘要:概述相對(duì)于幾乎是重寫了新版的更偏向于組件化。汲取了很多思想,路由即是組件,使路由更具聲明式,且方便組合。如果你習(xí)慣使用,那么一定會(huì)很快上手新版的。被一分為三。不止是否有意義參考資料遷移到關(guān)注點(diǎn)官方文檔 概述 react-router V4 相對(duì)于react-router V2 or V3 幾乎是重寫了, 新版的react-router更偏向于組件化(everything is comp...
摘要:瀏覽器端使用的和集成使用時(shí)會(huì)用到中路由分類基于提供的和事件來(lái)保持和的同步。路由剖析在上面的示例中是轉(zhuǎn)發(fā)的樞紐在這個(gè)中轉(zhuǎn)站有很多線路通過(guò)開關(guān)可以啟動(dòng)列車的運(yùn)行乘坐列車就可以發(fā)現(xiàn)新大陸。 引言 在使用react做復(fù)雜的spa開發(fā)中,開發(fā)中必不可少的就是react-router,它使用Lerna管理多個(gè)倉(cāng)庫(kù), 在browser端常使用的幾個(gè)如下所示 react-router 提供了路由的...
摘要:我們?cè)趦?nèi)部來(lái)渲染不同的組件我們這里采用哈希路由的方式,鑒于的渲染機(jī)制,我們需要把值綁定進(jìn)入內(nèi)部。 手挽手帶你學(xué)React入門三檔,帶你學(xué)會(huì)使用Reacr-router4.x,開始創(chuàng)建屬于你的React項(xiàng)目 什么是React-router React Router 是一個(gè)基于 React 之上的強(qiáng)大路由庫(kù),它可以讓你向應(yīng)用中快速地添加視圖和數(shù)據(jù)流,同時(shí)保持頁(yè)面與 URL 間的同步。通俗一...
摘要:中的包中的包主要有三個(gè)和。的理念上面提到的理念是一切皆組件以下統(tǒng)一稱組件。從這點(diǎn)來(lái)說(shuō)的確方便了不少,也迎合一切皆組件的理念。組件是中主要的組成單位,可以認(rèn)為是或的路由入口。將該標(biāo)示為嚴(yán)格匹配路由。的屬性追加一條。 2019年不知不覺已經(jīng)過(guò)去19天了,有沒有給自己做個(gè)總結(jié)?有沒有給明年做個(gè)計(jì)劃?當(dāng)然筆者已經(jīng)做好了明年的工作、學(xué)習(xí)計(jì)劃;同時(shí)也包括該系列博客剩下的博文計(jì)劃,目前還剩4篇:分別...
摘要:一般情況下,都是作為等其他子路由的上層路由,使用了,接收一個(gè)屬性,傳遞給消費(fèi)子組件。創(chuàng)建對(duì)象,兼容老瀏覽器,其他和沒有大區(qū)別總結(jié)分為四個(gè)包,分別為,其中是瀏覽器相關(guān),是相關(guān),是核心也是共同部分,是一些配置相關(guān)。 這篇文章主要講的是分析 react-router 源碼,版本是 v5.x,以及 SPA 路由實(shí)現(xiàn)的原理。 文章首發(fā)地址 單頁(yè)面應(yīng)用都用到了路由 router,目前來(lái)看實(shí)現(xiàn)路由有...
閱讀 3129·2021-11-23 09:51
閱讀 1993·2021-09-09 09:32
閱讀 1100·2019-08-30 15:53
閱讀 2969·2019-08-30 11:19
閱讀 2482·2019-08-29 14:15
閱讀 1448·2019-08-29 13:52
閱讀 565·2019-08-29 12:46
閱讀 2835·2019-08-26 12:18