该博客主要归纳于这里
1. 三个库
(1) history
整个路由原理的核心,里面集成了popState,pushState等等底层路由实现的原理方法。
(2) react-router
react-router-dom的核心,里面封装了Router,Route,Switch等核心组件,实现了从路由的改变到组件的更新的核心功能
(3) react-router-dom
在react-router的核心基础上,添加了用于跳转的Link组件,和history模式下的BrowserRouter和hash模式下的HashRouter组件等。
所谓BrowserRouter和HashRouter,也只不过用了history库中createBrowserHistory和createHashHistory方法
2. 单页面路由
单页面应用路由实现原理是:切换url,监听url变化,从而渲染不同的页面组件。
主要的方式有history模式和hash模式。
2.1 history模式
2.1.1 改变路由
history.pushState()方法用于在历史中添加一条记录。
添加新记录后,浏览器地址栏立刻显示新地址,但并不会跳转,甚至也不会检查地址是否存在,它只是成为浏览历史中的最新记录。
history.pushState(state,title,path)
- state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要可填null。
- title:新页面的标题,但是所有浏览器目前都忽略这个值,可填null。
- path:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个地址。
history.replaceState()方法用来修改history对象的当前记录,其他都与pushState()方法一模一样。
history.replaceState(state,title,path)
2.1.2 监听路由
history对象出现变化时,就会触发popstate事件。
用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。popstate事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮或者调用history.back()、history.forward()、history.go()方法。
window.addEventListener('popstate',function(e){
/* 监听改变 */
})
2.2 hash模式
2.2.1 改变路由
window.location.hash
通过window.location.hash 属性获取和设置 hash 值。
2.2.2 监听路由
onhashchange
window.addEventListener('hashchange',function(e){
/* 监听改变 */
})
3. history库
react-router路由离不开history库,history专注于记录路由history状态,以及path改变了,我们应该做什么。
在history模式下用popstate监听路由变化,在hash模式下用hashchange监听路由的变化。
接下来我们看Browser模式下的createBrowserHistory和Hash模式下的createHashHistory方法。
3.1 createBrowserHistory
Browser模式下路由的运行,一切都从createBrowserHistory开始。
这里就重点分析一下history.push方法
const push = (path, state) => {
const action = 'PUSH'
/* 1 创建location对象 */
const location = createLocation(path, state, createKey(), history.location)
/* 确定是否能进行路由转换,还在确认的时候又开始了另一个转变 ,可能会造成异常 */
transitionManager.confirmTransitionTo(location, action, getUserConfirmation, (ok) => {
if (!ok)
return
const href = createHref(location)
const { key, state } = location
if (canUseHistory) {
/* 改变 url */
globalHistory.pushState({ key, state }, null, href)
if (forceRefresh) {
window.location.href = href
} else {
/* 改变 react-router location对象, 创建更新环境 */
setState({ action, location })
}
} else {
window.location.href = href
}
})
}
history.push流程大致是首先生成一个最新的location对象,然后通过history.pushState改变浏览器当前的path,最后通过setState方法通知React-Router更新,并传递当前的location对象,由于这次url变化是history.pushState导致的,并不会触发popState方法,所以需要手动setState,触发组件更新。
3.2 createHashHistory
hash模式下和history模式下主要区别在于监听路由,push方法和replace方法。
监听路由通过hashchange,区别于history通过popstate。
在hash模式下 ,history.push底层是调用了window.location.href来改变路由。history模式下是通过pushstate。
history.replace底层是调用window.location.replace改变路由。history模式下是通过replacestate。
/* 对应 push 方法 */
const pushHashPath = (path) =>
window.location.hash = path
/* 对应replace方法 */
const replaceHashPath = (path) => {
const hashIndex = window.location.href.indexOf('#')
window.location.replace(
window.location.href.slice(0, hashIndex >= 0 ? hashIndex : 0) + '#' + path
)
}
4. 流程分析
当地址栏的URL发生改变,组件的更新渲染都经历了什么?
以history模式进行参考:
- url改变,调用事件监听方法popstate(),触发回调函数handlePopState()
- 触发history下的setstate方法,产生新的location对象
- 通知Router组件更新location并通过context传递
- Switch根据传递的更新流,匹配出符合的Route组件
- 最后由Route组件取出context内容,传递给渲染页面后渲染更新
当调用history.push()方法,切换路由,组件的更新渲染经历了什么?
还是以history模式进行参考:
- 首先调用history的push方法,通过history.pushState改变URL
- 剩下四步同上,也就是只有第一步不一样
5. 总结
history库提供了核心api,比如监听路由,更改路由的方法,以及保存路由状态state
react-router库提供路由渲染组件,路由唯一性匹配组件,重定向组件等功能组件