🤔 问题背景
目前保险业务用的是react技术栈,在开发过程中遇到了两个路由相关的奇怪问题:
【示例代码】
1⃣️.【问题1】在使用React—Router的basename功能的时候发现,无论是使用 HashRouter 还是 BrowserRouter,路由中有没有basename都能够匹配到对应的组件,哪怕我们已经给路由加上了 exact 即严格匹配规则.
2⃣️.【问题2】 在排查 【问题1👆】 的时候发现在浏览器的 Console 中对hash进行修改能正常切换页面,但是用history修改url却不会. 然而在代码中使用history API(history.push)修改路由却又是可以的😵 (猜测是react-router重写了history的API).
更改hash能正确切换路由
使用history.pushState不能切换路由
上面2个问题涉及到了【React-Router是如何匹配对应路径组件】,【React如何重写window.history API】 以及 【window.history API内部逻辑】等等问题. 所以我就借着解决上面两个问题的想法去了解了一下React-Router的源码。在下面讲解文章的过程中,顺带解释一下业务中遇到的问题以及原因.
😺 React-Router
React-router的源码同学们可以在 https://github.com/ReactTraining/react-router 下载.
项目结构
打开下载好的React-Router项目,如下图所示,可以看到最最核心部分是packages文件下的四个文件夹,分别是 react-router,react-router-config,react-router-dom,react-router-native,这个四个文件夹每个都可以作为单独的包进行发布,但是React把它们都放在一个仓库中,是一种典型的 monorepo 代码组织方式. 👉 什么是monorepo
- 那这四个文件夹分别有什么用呢?
- react-router:是
React-Router
的核心库,处理一些共用的逻辑.- react-router-config:是
React-Router
的配置处理,我们一般不需要使用- react-router-dom:浏览器上使用的库,会引用
react-router
核心库.- react-router-native:支持
React-Native
的路由库,也会引用react-router
核心库.
从上面的解释来看对于我们前端H5开发的同学来说最重要的就是 react-router 和
react-router-dom 这两个文件了.
- 打开 react-router-dom 根文件index.js
可以看到除了BrowserRouter,HashRouter,Link以及NavLink这四个组件是react-router-dom自己独立实现的,其他的(比如最基础的Router,Route,Switch组件等等)都来自react-router里的公用组件.
export {
MemoryRouter,
Prompt,
Redirect,
Route,
Router,
StaticRouter,
Switch,
generatePath,
matchPath,
withRouter,
useHistory,
useLocation,
useParams,
useRouteMatch
} from "react-router";
export { default as BrowserRouter } from "./BrowserRouter.js";
export { default as HashRouter } from "./HashRouter.js";
export { default as Link } from "./Link.js";
export { default as NavLink } from "./NavLink.js";
HashRouter & BrowserRouter
先来个简单的demo:
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom"
function App() {
return (
<Router>
<div>主菜单</div>
<Link to="/home">home</Link>
<br />
<Link to="/search">search</Link>
<hr />
<Switch>
<Route path="/home" component={Home} />
<Route path="/search" component={Search} />
</Switch>
</Router>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
可以看到,HashRouter/BrowserRouter 是所有组件Switch、Route、Link等组件的父组件,也是一个根组件,所以我们以它作为入口看下它实现了一些什么功能:
- HashRouter
import { createHashHistory as createHistory } from "history";
class HashRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />;
}
}
export default HashRouter;
- BrowserRouter
// 与HashRouter唯一的区别在这里,引入的是createBrowserHistory
// 而不是HashHistory
import { createBrowserHistory as createHistory } from "history";
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.hi