You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
在 Link 中,我们使用<a>标签来做跳转,但是 a 标签会使页面重新刷新,所以需要阻止 a 标签的默认行为,使用 context 中 history 的 push 方法
exportdefaultclassLinkextendsReact.Component<IProps>{render(){const{ to, children }=this.props;return(<RouterContext.Consumer>{(context)=>{return(<ahref={to}onClick={(event)=>{// 阻止a标签的默认行为event.preventDefault();context.history.push(to);}}>{children}</a>);}}</RouterContext.Consumer>);}}
前端路由
在 Web 前端单页面应用 SPA(Single Page Application)中,路由是描述 URL 和 UI 之间的映射关系,这种映射是单向的,即 URL 的改变会引起 UI 更新,无需刷新页面
如何实现前端路由
实现前端路由,需要解决两个核心问题
在前端路由的实现模式有两种模式,hash 和 history 模式,分别回答上述两个问题
hash 模式
hash 是 url 中 hash(#) 及后面的部分,常用锚点在页面内做导航,改变 url 中的 hash 部分不会引起页面的刷新
通过 hashchange 事件监听 URL 的改变。改变 URL 的方式只有以下几种:通过浏览器导航栏的前进后退、通过
<a>
标签、通过window.location
,这几种方式都会触发hashchange
事件history 模式
history 提供了
pushState
和replaceState
两个方法,这两个方法改变 URL 的 path 部分不会引起页面刷新通过 popstate 事件监听 URL 的改变。需要注意只在通过浏览器导航栏的前进后退改变 URL 时会触发
popstate
事件,通过<a>
标签和pushState
/replaceState
不会触发popstate
方法。但我们可以拦截<a>
标签的点击事件和pushState
/replaceState
的调用来检测 URL 变化,也是可以达到监听 URL 的变化,相对hashchange
显得略微复杂JS实现前端路由
基于 hash 实现
由于三种改变 hash 的方式都会触发
hashchange
方法,所以只需要监听hashchange
方法。需要在DOMContentLoaded
后,处理一下默认的 hash 值hash实现demo
基于 history 实现
因为 history 模式下,
<a>
标签和pushState
/replaceState
不会触发popstate
方法,我们需要对<a>
的跳转和pushState
/replaceState
做特殊处理。<a>
作点击事件,禁用默认行为,调用pushState
方法并手动触发popstate
的监听事件pushState
/replaceState
可以重写 history 的方法并通过派发事件能够监听对应事件history 实现 demo
react-router的理解
在 v4 之后,我们在 View 层直接从
react-router-dom
中引入BrowserRouter
/HashRouter
。BrowserRouter
/HashRouter
又分别使用了react-router
提供的 Router 组件和 history 提供的createBrowserHistory
/createHashHistory
方法。react-router v3/v4/v6的应用
v3、v4、v6
history
在上文中说到,
BrowserRouter
使用history库提供的createBrowserHistory
创建的history
对象改变路由状态和监听路由变化。❓那么 history 对象需要提供哪些功能讷?
listen
方法以及对应的清理监听unlisten
方法push
方法BrowserHistory
上述代码实现简单版本的 history,只有监听路由变化的
listen
/unlisten
方法以及改变路由的push
方法,详细的BrowserHistory源码HashHistory
和
BrowserHistory
一样,hashHistory
也是极简版,详细的hashHistory源码react-router丐版
Router
Router 接受一个 history 属性,用
history.listen
创建监听者,使用 context 传递 history 和location 数据Router源码
BrowserRouter/HashRouter
只是给 Router 组件传递 history 属性
BrowserRouter
HashRouter
BrowserRouter源码/HashRouter源码
Route
Route可以接收
component
/render
/children
,但是它们渲染的优先级是不一样的。v4/v5三个优先级不同
直接使用
Route
组件时,每个Route
组件都会被渲染,会根据路由规则进行判断是否需要把组件渲染出来,目前代码中使用的正则来做匹配Route源码
Link
在 Link 中,我们使用
<a>
标签来做跳转,但是 a 标签会使页面重新刷新,所以需要阻止 a 标签的默认行为,使用 context 中 history 的 push 方法Link源码
Switch
Route 组件的功能是只要 path 匹配上当前路由就渲染组件,也就意味着如果多个 Route 的 path 都匹配上了当前路由,这几个组件都会渲染,例如
/home/1
能够匹配上/home/1
和/home
,所以需要一个组件来控制匹配上一个 Route 就返回,所以 Switch 组件诞生了。它的功能就是即使多个 Route 的 path 都匹配上了当前路由,也只渲染第一个匹配上的组件。
要实现该功能,把 Switch 的 children 拿出来循环,找出第一个匹配的 child,记录下当前的 child ,把其他的 child 全部干掉
Switch源码
到现在 react-router 的核心组件以及 API 都实现完成,线上demo
总结
在本文中,从前端路由入手,分析了原生的 hash/history 的路由实现,react-router 底层依赖和上层使用,实现了简版的 react-router
需要注意的是,hash 模式下三种改变 url 的方法都会触发
hashchange
事件,而 history 模式下只有浏览器前进后退会触发popstate
,pushState
/replaceState
以及<a>
标签都不会。<a>
标签的默认行为会触发页面刷新,所以在实现路由时需要用e.preventDefault
阻止默认行为。The text was updated successfully, but these errors were encountered: