前言,首先为什么要做react-router源码解析呢,因为之前我们有一个需求,左侧导航栏检测到路由变化的时候展示不同的样式。 由于左侧导航栏没有包裹到路由Router组件中,所以左侧导航栏无法准确监测路由发生了变化,当时我想了想可以这样解决
一、我想象中的解决方案
方案一 onhashchange
onhashchange就可以检测了,但是项目中的路由是history。onhashchange不能在history模式下用,所以这一条算废了。
方案二、onpopstate
但是mdn告诉说事情不是这么简单。mdn明确说go与back可以检测到但是pushState与replaceState检测不到,由于项目中大量使用push这些方法。所以这一条也废弃了。但是为什么项目中history.push调用的时候react-router能感应到并做出路由切换,难道是黑科技吗,想到这里我就想对react-router源码进行解析为什么react-router可以感应到history.push方法。
请注意react-router-dom可以结构出来useHistory方法,执行该方法可得到已经封装好的history对象,该对象有push方法,实际上执行的是pushState方法,replace其实也就是replaceState方法,这里说的push方法大家可以理解成执行pushState方法。
方案三、redux
当使用history.push这些方法的时候往redux传入我们要跳转到哪个路由,然后谁要监测这个路由谁就订阅这个数据源。这是个好办法,但是由于项目中使用history.push的地方有点多,也就是入口多,需要在跳转的时候都要派发一次数据虽然可以解决监测路由的问题,但是写了大量重复代码。我们在方案4中采用了redux的思想。
方案四、路由守卫
我记得vue的路由中有路由守卫概念,当进入到路由之前的话,左侧导航栏变化不同的样式。我们用react-router实现路由守卫不就可以了吗,当组件检测到路由变化的时候这时候就dispatch派发路由数据。 下面就是我们当初写的一个很low的路由守卫
import { useEffect } from 'react';
import {BrowserRouter as Router, Switch, Route, useLocation} from 'react-router-dom';
import Home from './home';
const RouteGuard = (props)=> { const {pathname} = useLocation();useEffect(()=> {console.log('路由变化,',pathname);//可以dispatch派发pathname到redux中,然后左侧导航栏订阅这个数据就可以切换主题了,//这里也可以做路由权限的鉴定哈。如果没有权限写一些重定向的逻辑跳转到403页面。//这个return函数就相当于路由销毁的时候我们要干一些什么事情return ()=> {}});return props.children;
};
const App =()=> {return (<Router><RouteGuard><Switch><Route path='/' exact><Home/></Route></Switch></RouteGuard></Router>);
}
export default App;
二、react-router是怎么监听路由的改变的呢。
刚刚聊到react-router能够监测history.push方法执行并返回正确的路由。这里