#Hash 模式 vs History 模式
📌 一、概念对比:Hash 模式 vs History 模式
特性 | Hash 模式 | History 模式 |
---|---|---|
URL 样式 | http://example.com/#/home | http://example.com/home |
是否刷新页面 | ❌ 不会刷新(仅 hash 变化) | ✅ 通过 HTML5 API 控制,不刷新 |
原理 | window.onhashchange 监听 # 后的变化 | 使用 history.pushState() 和 popstate 事件 |
服务器要求 | ❌ 无需服务器配置 | ✅ 需要服务端支持(所有路由都重定向到 index.html ) |
SEO 友好 | ❌ 不友好 | ✅ 更友好 |
使用场景 | 本地项目、简单项目 | 需要完整前端路由的正式项目 |
💡 二、底层原理代码对比示例
🚀 1. Hash 模式核心原理示意
// 监听 hash 变化
window.addEventListener('hashchange', () => {
const hash = location.hash.slice(1); // 去掉 #
renderView(hash);
});
function renderView(route) {
const app = document.getElementById('app');
if (route === 'home') {
app.innerHTML = '<h2>首页</h2>';
} else if (route === 'about') {
app.innerHTML = '<h2>关于页</h2>';
} else {
app.innerHTML = '<h2>404</h2>';
}
}
// 初始化加载
window.addEventListener('load', () => {
const hash = location.hash.slice(1) || 'home';
renderView(hash);
});
✅ 不刷新页面,只监听 #
的变化。
🎯 2. History 模式核心原理示意
<!DOCTYPE html>
<html>
<body>
<a href="/home" data-link>首页</a>
<a href="/about" data-link>关于</a>
<div id="app"></div>
<script>
// 拦截 <a> 的点击,阻止默认跳转
document.addEventListener('click', e => {
if (e.target.matches('[data-link]')) {
e.preventDefault();
const path = e.target.getAttribute('href');
history.pushState({}, '', path); // 改变地址栏路径
render(path); // 渲染页面内容
}
});
// 后退/前进时触发
window.addEventListener('popstate', () => {
render(location.pathname);
});
// 页面初次加载
window.addEventListener('load', () => {
render(location.pathname);
});
function render(route) {
const app = document.getElementById('app');
if (route === '/home') app.innerHTML = '🏠 这是首页';
else if (route === '/about') app.innerHTML = 'ℹ️ 这是关于页';
else app.innerHTML = '❌ 404 Not Found';
}
</script>
</body>
</html>
🔍 这体现了 History 模式的本质:利用浏览器历史记录栈 + 路径变化不刷新页面,但刷新时依赖服务端路径处理。
✅ 使用了 history.pushState
,可以改变地址栏但不刷新页面。需要服务端配置:所有路径都返回 index.html
。
🔍 三、Vue Router 中的使用
// router/index.js
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
const routes = [
{ path: '/home', component: Home },
{ path: '/about', component: About }
]
// 💡 选择不同模式
const router = createRouter({
history: createWebHashHistory(), // Hash 模式
// history: createWebHistory(), // History 模式
routes
})
export default router
⚠️ 四、总结:什么时候用哪个?
场景 | 推荐模式 |
---|---|
开发阶段或无后端支持 | Hash 模式 |
SEO 需求 / 更自然的 URL | History 模式(需配置 Nginx/Apache) |
💡 核心区别:URL变化的本质机制不同
区别点 | Hash 模式 | History 模式 |
---|---|---|
触发方式 | 改变 location.hash (即 # 后的部分) | 使用 history.pushState() 或 replaceState() |
页面刷新? | ❌ 永远不会刷新 | ✅ 默认不刷新,但如果用户手动输入或刷新,就需要后端支持 |
URL 示例 | http://a.com/#/home | http://a.com/home |
浏览器行为 | hash 改变不会请求服务器 | history 改变,如果没有处理,浏览器会向服务器发起请求 |
📌 从浏览器行为来看本质区别的代码对比
✅ Hash 模式代码:
<!-- hash.html -->
<a href="#/home">首页</a>
<a href="#/about">关于</a>
<script>
window.addEventListener('hashchange', () => {
console.log('当前 hash:', location.hash);
});
</script>
📍点击链接时,浏览器不会向服务器发送请求,只是修改
#
后的字符串,触发hashchange
。
✅ History 模式代码:
<!-- history.html -->
<a href="/home" data-push="true">首页</a>
<a href="/about" data-push="true">关于</a>
<script>
document.addEventListener('click', e => {
if (e.target.dataset.push) {
e.preventDefault();
const path = e.target.getAttribute('href');
history.pushState({}, '', path); // 修改 URL,无刷新
console.log('当前路径:', location.pathname);
}
});
// 用户点击浏览器的“前进/后退”按钮时
window.addEventListener('popstate', () => {
console.log('用户前进/后退,当前路径:', location.pathname);
});
</script>
📍浏览器地址栏变化了,但页面没刷新。如果你手动刷新页面,浏览器就会尝试向
/about
发送真实的 HTTP 请求(如果没有服务端配置,会报 404 ❌)。
⚠️ 最明显区别验证方式 ✅
测试操作 | Hash 模式 | History 模式 |
---|---|---|
🔄 刷新页面 | 保持页面内容不变,浏览器不会请求服务器 | 如果没有后端配置,会报 404 |
📌 浏览器“后退” | 会触发 hashchange | 会触发 popstate |
👀 查看 Network 面板 | 没有任何请求 | 如果刷新页面或直接访问某个路径,会发送请求到该路径 |
🚧 模拟真实后端配置问题(最直观的对比)
✅ Hash 模式部署(如 GitHub Pages)
- 不管你访问什么
#/xxx
,实际请求的永远是index.html
- 所以不需要后端配置
⚠️ History 模式部署
- 用户访问
http://a.com/about
- 浏览器会发起请求:
GET /about
- 如果服务器没有设置“所有路径都返回 index.html”,就会 404
👀 所以 vue-router 的 history 模式 必须配合服务端 rewrite 规则 才能用!
🎯 总结成一句核心对比:
Hash 模式是前端控制 URL 变化,不涉及服务端请求;History 模式则是真正改变浏览器路径,需要服务端协助处理路径。