【vue-rouer源码】系列文章
- 【vue-router源码】一、router.install解析
- 【vue-router源码】二、createWebHistory、createWebHashHistory、createMemoryHistory源码解析
- 【vue-router源码】三、理解Vue-router中的Matcher
- 【vue-router源码】四、createRouter源码解析
- 【vue-router源码】五、router.addRoute、router.removeRoute、router.hasRoute、router.getRoutes源码分析
- 【vue-router源码】六、router.resolve源码解析
- 【vue-router源码】七、router.push、router.replace源码解析
- 【vue-router源码】八、router.go、router.back、router.forward源码解析
- 【vue-router源码】九、全局导航守卫的实现
- 【vue-router源码】十、isReady源码解析
- 【vue-router源码】十一、onBeforeRouteLeave、onBeforeRouteUpdate源码分析
- 【vue-router源码】十二、useRoute、useRouter、useLink源码分析
- 【vue-router源码】十三、RouterLink源码分析
- 【vue-router源码】十四、RouterView源码分析
前言
【vue-router源码】系列文章将带你从0开始了解vue-router
的具体实现。该系列文章源码参考vue-router v4.0.15
。
源码地址:https://github.com/vuejs/router
阅读该文章的前提是你最好了解vue-router
的基本使用,如果你没有使用过的话,可通过vue-router官网学习下。
在vue-router 4.x
中创建router
时,需要使用createWebHistory
、createWebHashHistory
、createMemoryHistory
中的一个创建一个history
,这篇文章将就这三个函数进行解析。
使用
import {
createWebHistory, createRouter } from 'vue-router'
const routerHistory = createWebHistory()
const router = createRouter({
history: routerHistory,
routes: [ ... ]
})
createWebHistory
createWebHistory
源码所处位置:src/history/html5.ts
。
首先来看createWebHistory
的参数,函数可以接受一个base
字符串可选参数,该参数提供了一个基础路径。
在createWebHistory
中首先会调用normalizeBase
函数对传入的base
进行标准化。
base = normalizeBase(base)
来看下base
标准化的过程:
export function normalizeBase(base?: string): string {
if (!base) {
// 浏览其环境下尝试获取base标签的href属性
if (isBrowser) {
const baseEl = document.querySelector('base')
base = (baseEl && baseEl.getAttribute('href')) || '/'
// 去除htttp(s)://xxx/,如https://example.com/folder/ --> /folder/
base = base.replace(/^\w+:\/\/[^\/]+/, '')
} else {
base = '/'
}
}
// 确保base的前导/
if (base[0] !== '/' && base[0] !== '#') base = '/' + base
return removeTrailingSlash(base)
}
如果没有配置base
的话,在浏览器环境下会尝试获取<base>
标签的href
属性作为base
,如果没有<base>
标签或<base>
标签的href
属性没有值,base
取/
,然后又对base
进行了reaplce(/^\w+:\/\/[^\/]+/, '')
操作,该操作是去除base
的http(s)://xxx
部分(如果base
是https://example.com/floder/child
,base
最终会变成/floder/child
);非浏览器环境下,base
直接取/
。在最后会将base
的末尾/
去除,然后返回base
,这样做的目的是后续我们可以通过base + fullPath
的形式建立一个href
。
base
标准化后,会声明一个historyNavigation
和historyListeners
变量:
const historyNavigation = useHistoryStateNavigation(base)
const historyListeners = useHistoryListeners(
base,
historyNavigation.state,
historyNavigation.location,
historyNavigation.replace
)
这两个变量是什么呢?接下来看下useHistoryStateNavigation()
、useHistoryListeners()
的实现。
先看useHistoryStateNavigation
:
function useHistoryStateNavigation(base: string) {
// 获取window.history、window.location
const {
history, location } = window
const currentLocation: ValueContainer<HistoryLocation> = {
value: createCurrentLocation(base, location),
}
const historyState: ValueContainer<StateEntry> = {
value: }
// 如果history.state是空的,构建一条新的历史记录
if (!historyState.value) {
changeLocation(
currentLocation.value,
{
back: null,
current: currentLocation.value,
forward: null,
position: history.length - 1,
replaced: true,
scroll: null,
},
true
)
}
// 修改历史记录
function changeLocation(
to: HistoryLocation,
state: StateEntry,
replace: boolean
): void {
const hashIndex = base.indexOf('#')
// 获取url,作为history.replaceState/pushState的参数
// 如果hashIndex > -1,url = `{location.host && document.querySelector('base') ? base : base字符串#及后面字符}${to}`
// 否则 url = `${location.protocol}//${location.host}${base}${to}`
co