摘要:要實現的功能我們使用開發項目的時候,基本上是單頁面應用,也就離不開路由。路由看似神秘,當我們簡單的模擬一下它的核心功能后,發現也就這么回事兒。實現的邏輯是,返回中跟匹配到的第一個孩子。
1 要實現的功能
我們使用React開發項目的時候,基本上是單頁面應用,也就離不開路由。路由看似神秘,當我們簡單的模擬一下它的核心功能后,發現也就這么回事兒。本文就詳細的介紹一下react-router-dom 的 HashRouter的核心實現邏輯。
本文實現的功能主要包含:
HashRouter
Route
Link
MenuLink
Switch
Redirect
2 實現的邏輯先不說代碼是怎樣寫的,先上圖,讓大家看一下這個HashRouter到底是個什么東東:
好吧,肯定有人會說這些圈圈又是什么東東呀,客官不要著急,待我慢慢解釋:
HashRouter是一個大的容器,它控制著他自己到底渲染成什么樣子,那么它是通過什么控制的呢,看它的名字就能猜出來,那就是window.location.hash。
當HashRouter開始渲染的時候就會拿它自己身上的pathname屬性跟它肚子里的Route的path進行匹配,匹配上的話,就會渲染Route的component對應的組件。
Link是怎樣切換路由的呢,很簡單,就是通過this.props.history.push(path)來改變HashRouter中的pathname屬性,進而驅動Route們 進行重新渲染,再次匹配我們的路由,最終實現路由的切換。
介紹了一下簡單的邏輯,接下來我們就看一下具體是怎樣實現的吧,如下圖:
HashRouter是一個繼承了React.Component的類,這個類上的state包括location,監聽著hash的變化以驅動Route組件的重新渲染,另外還有一個history屬性,可以切換頁面的路由。
本文要實現的功能中包括Route、Link、MenuLink、Switch、 Redirect,其中Route的是基礎是核心,MenuLink和某些有特定邏輯的渲染都是在Route的基礎上實現的。
Route組件上可以接收三種變量,包括component、render、children,其中render、children是都是函數,render是根據特定的邏輯渲染元素,children是用來渲染MenuLink,這兩個函數都接收當前路由的props,函數的返回值是要渲染的元素。
Switch實現的邏輯是,返回children中跟hash匹配到的第一個“孩子”。
3 具體的代碼邏輯 (1) HashRouterHashRouter將window.loacation.hash跟自己的state掛鉤,通過改變自己的state驅動頁面的重新渲染。
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) || "/", // 當前頁面的hash值 state: {} //保存的狀態 } }; } // 定義上下文的變量類型 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; // 渲染頁面元素 } componentDidMount() { window.location.hash = window.location.hash.slice(1) || "/"; // 監聽window的hash的變化,驅動頁面的重新刷新 window.addEventListener("hashchange", () => { this.setState({ location: { ...this.state.location, pathname: window.location.hash.slice(1) || "/" } }); }) } }(2) Route
Route的渲染核心邏輯就是將自己的path和當前頁面的hash進行匹配,匹配上了就渲染相應的元素,匹配不上就什么都不渲染。
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() { // 解構傳入Route的props let {path, component: Component, render, children} = this.props; // 解構上下文的屬性 let {location, history} = this.context; let props = { location, history }; // 將傳入Route的path和當前的hash進行匹配 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) => { // 獲取匹配到的參數 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,驅動重新渲染。
import React, {Component} from "react"; import PropTypes from "prop-types"; export default class Redirect extends Component { // 定義上下文context的Type static contextTypes = { history: PropTypes.object } componentDidMount() { // 跳轉到目標路由 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}) => { // 如果匹配到了,就給當前組件一個激活狀態的className return(5) Link( {children} ) }/> }
Link就是渲染成一個a標簽,然后給一個點擊事件,點擊的時候更改HashRouter的狀態,驅動重新渲染。
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)) { // 將匹配到的第一個元素返回 return child; } } return null } }4 寫在最后
好了,這幾個功能介紹完了,你是否對HashRouter的原理有所了解了呢?本文只是貼出部分代碼,如果有需要請看demo可以手動體驗一下哦。
參考文獻:
react-router
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/94153.html
摘要:概述相對于幾乎是重寫了新版的更偏向于組件化。汲取了很多思想,路由即是組件,使路由更具聲明式,且方便組合。如果你習慣使用,那么一定會很快上手新版的。被一分為三。不止是否有意義參考資料遷移到關注點官方文檔 概述 react-router V4 相對于react-router V2 or V3 幾乎是重寫了, 新版的react-router更偏向于組件化(everything is comp...
摘要:我們在內部來渲染不同的組件我們這里采用哈希路由的方式,鑒于的渲染機制,我們需要把值綁定進入內部。 手挽手帶你學React入門三檔,帶你學會使用Reacr-router4.x,開始創建屬于你的React項目 什么是React-router React Router 是一個基于 React 之上的強大路由庫,它可以讓你向應用中快速地添加視圖和數據流,同時保持頁面與 URL 間的同步。通俗一...
摘要:中的包中的包主要有三個和。的理念上面提到的理念是一切皆組件以下統一稱組件。從這點來說的確方便了不少,也迎合一切皆組件的理念。組件是中主要的組成單位,可以認為是或的路由入口。將該標示為嚴格匹配路由。的屬性追加一條。 2019年不知不覺已經過去19天了,有沒有給自己做個總結?有沒有給明年做個計劃?當然筆者已經做好了明年的工作、學習計劃;同時也包括該系列博客剩下的博文計劃,目前還剩4篇:分別...
摘要:一般情況下,都是作為等其他子路由的上層路由,使用了,接收一個屬性,傳遞給消費子組件。創建對象,兼容老瀏覽器,其他和沒有大區別總結分為四個包,分別為,其中是瀏覽器相關,是相關,是核心也是共同部分,是一些配置相關。 這篇文章主要講的是分析 react-router 源碼,版本是 v5.x,以及 SPA 路由實現的原理。 文章首發地址 單頁面應用都用到了路由 router,目前來看實現路由有...
閱讀 3118·2021-11-23 09:51
閱讀 1983·2021-09-09 09:32
閱讀 1094·2019-08-30 15:53
閱讀 2965·2019-08-30 11:19
閱讀 2475·2019-08-29 14:15
閱讀 1443·2019-08-29 13:52
閱讀 560·2019-08-29 12:46
閱讀 2827·2019-08-26 12:18