各位观众老爷,大家好!今天咱们来聊聊 Vue Router 里的两位老朋友:createWebHistory
和 createWebHashHistory
。别看名字长,其实它们就是 Vue 应用里 URL 的管家,负责管理你的页面地址,让你在不同页面间跳来跳去,体验丝滑顺畅。
咱们不搞虚头巴脑的,直接深入源码,看看这两位管家到底是怎么干活的。
一、 createWebHistory
: 优雅的绅士
createWebHistory
,顾名思义,创造一个基于 Web History API 的路由历史。 这位爷追求优雅,它使用浏览器的 history.pushState
和 history.replaceState
方法来修改 URL,而且不会引起页面刷新。 这样,你的 URL 看上去就像正常的网站地址一样,比如 https://example.com/users/123
。
1. 源码结构
先来看看 createWebHistory
函数的大致结构(简化版,去掉了类型定义和一些边界情况处理):
function createWebHistory(base) {
if ( base === void 0 ) base = '';
const history = window.history;
//确保base以'/'开头和结尾
const baseRef = ref(base);
const current = ref(getLocation(baseRef.value)); //当前路由
function push(to, data) {
const url = resolve(to); //解析to成完整的url
history.pushState(data, '', url);
current.value = getLocation(baseRef.value); //更新当前路由
}
function replace(to, data) {
const url = resolve(to);
history.replaceState(data, '', url);
current.value = getLocation(baseRef.value);
}
function listen(callback) {
const popStateHandler = () => {
current.value = getLocation(baseRef.value);
callback({
type: NavigationType.pop,
state: history.state
});
};
window.addEventListener('popstate', popStateHandler);
return () => {
window.removeEventListener('popstate', popStateHandler);
};
}
function getLocation(base) {
let path = window.location.pathname;
if (base && path.startsWith(base)) {
path = path.slice(base.length);
}
return path + window.location.search + window.location.hash;
}
function resolve(to) {
if (typeof to === 'string') {
return baseRef.value + to
}
// 省略处理对象形式的to,比如 { path: '/users/123', query: { ... } }
return baseRef.value + to.path
}
return {
location: current,
base: baseRef,
push,
replace,
listen
};
}
2. 核心功能解析
-
getLocation(base)
: 这个函数从window.location
获取当前 URL,并移除base
部分(如果存在)。base
通常是你的应用部署的根路径,例如/app/
。 这个函数确保返回的路径是相对于你的应用的。- 举个例子:如果
window.location.pathname
是/app/users/123
,且base
是/app/
,那么getLocation
会返回/users/123
。
- 举个例子:如果
-
push(to, data)
: 这个函数使用history.pushState
来创建一个新的历史记录。to
是目标 URL,data
是与该历史记录关联的数据(可以是任何 JavaScript 对象,通常用于存储路由状态)。- 关键点:
pushState
不会引起页面刷新,但会在浏览器的历史记录中添加一条新的记录。
- 关键点:
replace(to, data)
: 与push
类似,但使用history.replaceState
来替换当前的浏览历史记录,而不是添加新的记录。- 关键点:
replaceState
同样不会引起页面刷新,但会用新的 URL 和数据覆盖当前的历史记录。
- 关键点:
-
listen(callback)
: 这个函数监听popstate
事件。 当用户点击浏览器的前进/后退按钮时,会触发popstate
事件。listen
函数会调用callback
,并传递一个包含导航类型(pop
)和状态(history.state
)的对象。- 关键点:
popstate
事件是createWebHistory
监听 URL 变化的关键。 浏览器会自动处理前进/后退操作,popstate
事件会通知你的应用 URL 已经改变。
- 关键点:
3. 工作流程
- 初始化:
createWebHistory
函数会被调用,传入一个base
参数(可选)。 - 首次加载:
getLocation
函数获取当前 URL,并将其设置为current
响应式变量的值。 - 导航:
- 当你的应用调用
router.push(to)
或router.replace(to)
时,push
或replace
函数会被调用。 push
或replace
函数会调用history.pushState
或history.replaceState
来更新 URL,并更新current
响应式变量的值。
- 当你的应用调用
- 后退/前进:
- 当用户点击浏览器的前进/后退按钮时,会触发
popstate
事件。 listen
函数监听popstate
事件,并调用回调函数。- 回调函数会更新
current
响应式变量的值,从而触发 Vue 组件的重新渲染。
- 当用户点击浏览器的前进/后退按钮时,会触发
二、 createWebHashHistory
: 朴实的农民
createWebHashHistory
比较实在,它使用 URL 的 hash 部分(#
符号后面的内容)来模拟路由。 它的 URL 看起来像这样:https://example.com/#/users/123
。
1. 源码结构
function createWebHashHistory(base) {
if ( base === void 0 ) base = '';
const history = window.history;
const baseRef = ref(base);
const current = ref(getHash());
function push(to, data) {
const url = resolve(to);
writeHash(url);
current.value = getHash();
}
function replace(to, data) {
const url = resolve(to);
history.replaceState(data, '', url); //这里需要注意,replaceState只是修改了整个url,hash部分不变
current.value = getHash();
}
function listen(callback) {
const hashChangeHandler = () => {
current.value = getHash();
callback({
type: NavigationType.hashchange,
state: history.state
});
};
window.addEventListener('hashchange', hashChangeHandler);
return () => {
window.removeEventListener('hashchange', hashChangeHandler);
};
}
function resolve(to) {
if (typeof to === 'string') {
return baseRef.value + '#' + to
}
return baseRef.value + '#' + to.path
}
function getHash() {
// We can't use location.hash here because it's not
// consistent across browsers - Firefox decodes before
// returning the value.
let href = window.location.href;
const index = href.indexOf('#');
// Empty string if no hash, otherwise the entire hash
return index < 0 ? '' : href.slice(index);
}
function writeHash(path) {
window.location.hash = path;
}
return {
location: current,
base: baseRef,
push,
replace,
listen
};
}
2. 核心功能解析
getHash()
: 这个函数从window.location.href
获取 URL 的 hash 部分。 注意,它不直接使用window.location.hash
,因为不同浏览器对window.location.hash
的处理方式可能不一致。writeHash(path)
: 这个函数设置window.location.hash
的值。 这会改变 URL 的 hash 部分,但不会引起页面刷新。push(to, data)
: 这个函数调用writeHash
来更新 URL 的 hash 部分,并更新current
响应式变量的值。data
参数在这里其实没有被使用,因为 hash 模式下,history.state
不太好管理,所以 Vue Router 简化了这个逻辑。replace(to, data)
: 这个函数调用history.replaceState
修改整个url,但hash部分是不变的,同时更新current
响应式变量的值。data
参数在这里其实没有被使用。-
listen(callback)
: 这个函数监听hashchange
事件。 当 URL 的 hash 部分发生改变时,会触发hashchange
事件。listen
函数会调用callback
,并传递一个包含导航类型(hashchange
)和状态(history.state
)的对象。- 关键点:
hashchange
事件是createWebHashHistory
监听 URL 变化的关键。 浏览器会自动处理 hash 的改变,hashchange
事件会通知你的应用 URL 已经改变。
- 关键点:
3. 工作流程
- 初始化:
createWebHashHistory
函数会被调用,传入一个base
参数(可选)。 - 首次加载:
getHash
函数获取当前 URL 的 hash 部分,并将其设置为current
响应式变量的值。 - 导航:
- 当你的应用调用
router.push(to)
或router.replace(to)
时,push
或replace
函数会被调用。 push
函数会调用writeHash
来更新 URL 的 hash 部分,并更新current
响应式变量的值。replace
函数调用history.replaceState
修改整个url,但hash部分是不变的,同时更新current
响应式变量的值。
- 当你的应用调用
- 后退/前进:
- 当用户点击浏览器的前进/后退按钮时,如果 hash 发生了改变,会触发
hashchange
事件。 listen
函数监听hashchange
事件,并调用回调函数。- 回调函数会更新
current
响应式变量的值,从而触发 Vue 组件的重新渲染。
- 当用户点击浏览器的前进/后退按钮时,如果 hash 发生了改变,会触发
三、 差异对比
为了更清楚地理解两者的区别,我们用表格来总结一下:
特性 | createWebHistory |
createWebHashHistory |
---|---|---|
URL 格式 | https://example.com/users/123 |
https://example.com/#/users/123 |
使用 API | history.pushState , history.replaceState , popstate |
window.location.hash , hashchange , history.replaceState |
SEO 友好度 | 更好,更符合传统网站的 URL 结构 | 较差,搜索引擎可能忽略 hash 部分 |
服务器配置 | 需要服务器配置,以确保所有路径都指向你的应用 | 不需要服务器配置,因为 hash 部分不会发送到服务器 |
兼容性 | 现代浏览器 | 几乎所有浏览器 |
核心监听事件 | popstate |
hashchange |
四、 选择哪一个?
createWebHistory
: 如果你的应用需要更好的 SEO,并且你能够配置你的服务器,那么createWebHistory
是一个更好的选择。 它的 URL 结构更清晰,更符合传统网站的习惯。createWebHashHistory
: 如果你的应用不需要 SEO,或者你无法配置你的服务器(例如,你的应用部署在 GitHub Pages 上),那么createWebHashHistory
是一个更简单、更兼容的选择。
五、 总结
createWebHistory
和 createWebHashHistory
都是 Vue Router 中用于管理 URL 的工具。 createWebHistory
使用 Web History API,提供更优雅的 URL 结构,但需要服务器配置。 createWebHashHistory
使用 URL 的 hash 部分,不需要服务器配置,但 SEO 较差。
理解了它们的实现细节,你就能更好地选择适合你的应用的路由模式,并能更深入地理解 Vue Router 的工作原理。
希望今天的讲座对大家有所帮助! 如果有什么疑问,欢迎随时提问。 咱们下期再见!