vue-router 中 history 与 hash 模式深度对比:原理、差异与选型
在 vue-router 中,路由模式决定了 URL 的表现形式和页面导航的实现逻辑。目前 vue-router 支持两种核心路由模式:hash 模式(默认)和history 模式,两者在 URL 格式、底层原理、兼容性和部署要求上存在显著差异。
本文将从 “实现原理”“核心差异”“适用场景” 三个维度,全面解析两种模式的区别,帮你在项目开发中做出正确选择。
一、先明确:两种模式的本质区别
hash 模式和 history 模式的核心目标一致 —— 实现 “单页应用(SPA)的无刷新导航”,但实现方式完全不同:
-
hash 模式:基于 URL 中的 “哈希值”(
#及其后面的内容)实现,通过监听哈希值变化触发页面更新; -
history 模式:基于 HTML5 的
History API(pushState和replaceState)实现,通过操作浏览器历史记录实现无刷新导航,URL 中无#。
二、底层实现原理拆解
要理解两种模式的差异,首先需要掌握它们的底层实现逻辑 —— 这是后续所有差异的根源。
1. hash 模式:依赖 URL 哈希值的 “锚点特性”
(1)哈希值的核心特性
URL 中的#(哈希符)后面的部分称为 “哈希值”(如https://xxx.com/#/home中的/home),它有两个关键特性:
-
哈希值变化不会触发页面刷新:当修改 URL 中的哈希值时,浏览器只会更新地址栏的 URL,不会向服务器发送新的 HTTP 请求,因此页面不会刷新;
-
哈希值变化会触发
hashchange事件:浏览器提供了window.onhashchange事件,当哈希值变化时,可通过该事件监听并执行自定义逻辑(如更新页面内容)。
(2)hash 模式的实现流程
vue-router 的 hash 模式正是利用了哈希值的这两个特性,实现无刷新导航:
-
初始化路由:当应用加载时,vue-router 解析 URL 中的哈希值(如
#/home),匹配对应的路由规则,渲染对应的组件; -
监听哈希变化:通过
window.addEventListener('hashchange', handler)监听哈希值变化; -
导航触发:当用户点击
<router-link to="/about">时,vue-router 不会触发页面跳转,而是修改 URL 的哈希值(如从#/home改为#/about); -
更新视图:哈希值变化触发
hashchange事件,vue-router 在事件回调中解析新的哈希值,匹配路由并更新页面组件(无刷新)。
(3)示例 URL
hash 模式的 URL 格式:https://yourdomain.com/#/home、https://yourdomain.com/#/user/123,URL 中始终包含#。
2. history 模式:依赖 HTML5 的 History API
(1)History API 的核心能力
HTML5 新增的History API为浏览器历史记录提供了更灵活的操作方式,核心是两个方法:
-
history.pushState(state, title, url):向浏览器历史记录中 “添加一条新记录”,修改地址栏 URL,但不发送 HTTP 请求,也不触发页面刷新; -
history.replaceState(state, title, url):“替换当前的历史记录”,同样不发送 HTTP 请求,仅修改地址栏 URL; -
两者的区别:
pushState会增加一条历史记录(可通过 “后退” 按钮返回上一页),replaceState会覆盖当前历史记录(无法通过 “后退” 返回修改前的 URL)。
(2)history 模式的实现流程
vue-router 的 history 模式通过History API实现无刷新导航,流程如下:
-
初始化路由:应用加载时,vue-router 解析当前 URL(如
https://yourdomain.com/home),匹配路由规则并渲染组件; -
监听历史变化:通过
window.addEventListener('popstate', handler)监听浏览器的 “前进 / 后退” 按钮事件(popstate事件仅在点击前进 / 后退或调用history.back()/history.forward()时触发,pushState/replaceState不会触发); -
导航触发:用户点击
<router-link to="/about">时,vue-router 调用history.pushState(null, null, '/about'),修改地址栏 URL 为https://yourdomain.com/about,不发送 HTTP 请求; -
更新视图:手动调用路由匹配逻辑,根据新 URL 渲染对应的组件(无刷新);
-
处理前进 / 后退:当用户点击浏览器 “后退” 按钮时,触发
popstate事件,vue-router 解析当前 URL,更新视图。
(3)示例 URL
history 模式的 URL 格式:https://yourdomain.com/home、https://yourdomain.com/user/123,URL 与传统后端路由格式一致,无#。
三、核心差异对比(7 大维度)
为了更直观地理解两种模式的区别,我们从 URL 表现、兼容性、部署要求等 7 个核心维度进行对比:
| 对比维度 | hash 模式 | history 模式 |
|---|---|---|
| URL 格式 | 包含#(如https://xxx.com/#/home) |
无#(如https://xxx.com/home),URL 更美观 |
| 底层原理 | 依赖 URL 哈希值的hashchange事件 |
依赖 HTML5 的History API(pushState/replaceState) |
| 兼容性 | 支持所有浏览器(包括 IE8 及以下) | 仅支持 HTML5 兼容的浏览器(IE10 及以上) |
| 后端依赖 | 无:刷新页面时,浏览器仅向服务器请求https://xxx.com/(哈希值不会发送到服务器) |
有:刷新页面时,浏览器会向服务器请求https://xxx.com/home,需后端配置 “所有路由指向 index.html”,否则返回 404 |
| 锚点功能 | 无法使用原生锚点(#已被路由占用),需自定义锚点逻辑 |
支持原生锚点(如https://xxx.com/home#section1) |
| 历史记录操作 | 仅能通过哈希值变化管理历史,灵活性低 | 可通过pushState/replaceState灵活操作历史记录,支持自定义状态数据 |
| SEO 友好性 | 略差:部分搜索引擎可能忽略#后的内容 |
更好:URL 与传统路由一致,搜索引擎更易识别(需配合后端 SSR 或静态生成优化) |
四、部署要求:history 模式需后端配置(关键!)
两种模式的部署差异是项目开发中最容易踩坑的点,尤其是 history 模式 —— 若后端未正确配置,会导致 “刷新页面返回 404” 的问题。
1. hash 模式的部署:无需特殊配置
hash 模式下,无论用户访问https://xxx.com/#/home还是https://xxx.com/#/user/123,当页面刷新时,浏览器只会向服务器发送https://xxx.com/的请求(哈希值#/home不会被发送到服务器)。因此,只需将前端打包后的文件(如index.html、css、js)部署到服务器的根目录,无需任何后端配置,刷新页面不会出现 404。
2. history 模式的部署:必须配置后端
history 模式下,当用户访问https://xxx.com/home并刷新页面时,浏览器会向服务器发送GET /home的请求 —— 若后端未配置 “/home路由指向index.html”,则服务器会返回 404 错误。
因此,history 模式部署时,需后端做 “所有路由指向前端入口文件(如index.html)” 的配置,让前端 vue-router 接管路由匹配。以下是常见后端的配置示例:
(1)Nginx 配置
在nginx.conf或站点配置文件中,添加try_files指令,将所有请求转发到index.html:
server {
listen 80;
server_name yourdomain.com; # 你的域名
root /usr/share/nginx/html; # 前端打包文件的根目录
index index.html; # 入口文件
# 关键配置:所有请求都转发到index.html
location / {
try_files $uri $uri/ /index.html;
}
}
(2)Apache 配置
在项目根目录创建.htaccess文件,添加以下规则:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
(3)Node.js(Express)配置
在 Express 项目中,使用express-history-api-fallback中间件:
const express = require('express');
const history = require('express-history-api-fallback');
const app = express();
const root = __dirname + '/public'; // 前端打包文件目录
app.use(express.static(root));
app.use(history('index.html', { root: root })); // 所有请求转发到index.html
app.listen(3000, () => {
console.log('Server running on port 3000');
});
五、选型建议:如何选择适合的模式?
两种模式没有绝对的 “优劣”,选择的核心是 “项目需求” 和 “技术环境”,以下是具体的选型建议:
1. 优先选择 hash 模式的场景
-
兼容性要求高:项目需要支持 IE8 及以下浏览器(如面向政府、企业的老旧系统);
-
快速部署:无后端开发团队支持,或后端无法配合配置(如静态站点部署到 GitHub Pages,默认不支持 history 模式的后端配置);
-
简单需求:项目无需美观的 URL,仅需实现基础的 SPA 导航功能。
2. 优先选择 history 模式的场景
-
URL 美观需求:项目对 URL 格式有要求(如官网、移动端应用),需要去掉
#,提升用户体验; -
锚点功能需求:项目需要使用原生锚点(如长页面导航到指定章节);
-
SEO 优化需求:项目需要更好的 SEO 表现(配合 SSR 或静态生成,如 Nuxt.js 项目);
-
技术环境允许:后端团队可配合配置路由转发,且目标浏览器支持 HTML5(IE10 及以上,现代浏览器均支持)。
六、常见问题与解决方案
1. history 模式刷新页面 404?
原因:刷新时浏览器向服务器请求具体路由(如/home),后端未配置转发到index.html;
解决方案:按上文 “history 模式的部署” 配置后端(Nginx/Apache/Node.js),将所有路由指向index.html。
2. hash 模式如何实现锚点功能?
原因:#已被路由占用,原生锚点(如#section1)会被解析为路由;
解决方案:自定义锚点逻辑,通过scrollTo或第三方库(如vue-scrollto)实现页面滚动到指定位置。
3. 如何在 vue-router 中切换模式?
在创建 vue-router 实例时,通过mode选项指定模式:
// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '@/views/Home';
Vue.use(Router);
export default new Router({
mode: 'hash', // 可选值:'hash'(默认)或 'history'
routes: [
{ path: '/', name: 'Home', component: Home }
]
});
七、总结
hash 模式和 history 模式是 vue-router 实现 SPA 无刷新导航的两种核心方式,核心差异源于底层实现原理:
-
hash 模式:兼容性强、无需后端配置,但 URL 含
#,美观度和功能灵活性较低; -
history 模式:URL 美观、支持原生锚点和 SEO 优化,但依赖 HTML5、需后端配置,兼容性稍弱。
在实际项目中,若后端可配合配置且无需支持老旧浏览器,优先选择 history 模式;若需兼容 IE8 及以下或无后端支持,选择 hash 模式即可。理解两种模式的原理和差异,能帮助我们在项目开发中避免部署错误,提升用户体验。
更多推荐


所有评论(0)