深入理解 Vue Router 中 Hash 模式 和 History 模式 的核心区别、底层原理、使用场景及部署注意事项,我会从「本质→原理→实战→对比→问题解决」全维度超详细讲解,结合你的项目场景(比如之前部署 History 模式报 404)帮你彻底吃透。
一、先搞懂:前端路由的核心本质
Vue 是单页应用(SPA),整个项目只有一个 index.html 文件。前端路由的核心目的:通过 JavaScript 监听 URL 变化,不刷新页面的前提下,动态渲染不同的组件,模拟 "多页面跳转" 的体验(无需向后端发起新的页面请求)。
Hash 模式和 History 模式是实现前端路由的两种技术方案,核心差异在于「URL 表现形式」「底层实现原理」「服务器部署要求」。
二、Hash 模式(哈希模式)
1. 核心定义
Hash 模式是利用 URL 中 # 后面的「哈希值」实现路由跳转,# 是浏览器的锚点标识,哈希值不会被发送到服务器。
- 示例 URL:
http://yourdomain.com/#/proOrder - 其中
#/proOrder就是哈希路由,#后面的内容完全由前端控制。
2. 底层实现原理
(1)核心 API:window.onhashchange
浏览器天生支持监听 # 后面内容的变化,触发 hashchange 事件,前端可在该事件中判断哈希值,渲染对应组件:
javascript
// 原生 JS 模拟 Hash 路由核心逻辑
window.addEventListener('hashchange', () => {
// 获取当前哈希值(去掉 #)
const hash = window.location.hash.slice(1);
// 根据哈希值渲染不同组件
if (hash === '/proOrder') {
renderProOrderComponent();
} else if (hash === '/home') {
renderHomeComponent();
}
});
(2)路由跳转的本质
- 点击
<router-link to="/proOrder">时,Vue Router 会修改window.location.hash = '#/proOrder'; - 该操作只会改变 URL 的哈希部分,不会触发浏览器的页面刷新,也不会向服务器发请求。
3. Vue Router 中配置 Hash 模式
typescript
// router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [/* 你的路由规则 */];
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL), // 核心:Hash 模式
routes
});
export default router;
4. 核心特点(优缺点)
| 优点 | 缺点 |
|---|---|
1. 无需后端配置 :哈希值不发服务器,直接访问 http://xxx.com/#/proOrder 不会 404;2. 兼容性极好 :支持所有浏览器(包括 IE6/7/8);3. 开发便捷:本地 / 线上部署无需协调后端; |
1. URL 不美观 :带 # 符号,用户体验略差;2. SEO 不友好 :部分搜索引擎爬虫会忽略 # 后的内容,影响页面收录;3. 锚点冲突 :若页面内有原生锚点(如 <a href="#top">),会和路由哈希冲突; |
三、History 模式(HTML5 历史模式)
1. 核心定义
History 模式基于 HTML5 新增的 History API 实现,URL 无 #,和传统后端路由的 URL 格式一致,哈希值会被完整发送到服务器。
- 示例 URL:
http://yourdomain.com/proOrder(和后端路由格式完全一致) - 该模式是现代前端项目的主流选择(追求 URL 美观、SEO 友好)。
2. 底层实现原理
(1)核心 API:HTML5 History API
history.pushState(state, title, url):修改 URL 且不发送请求,添加一条历史记录;history.replaceState(state, title, url):修改 URL 且不发送请求,替换当前历史记录;window.onpopstate:监听浏览器的「回退 / 前进」按钮,触发状态变化。
(2)路由跳转的本质
javascript
// 原生 JS 模拟 History 路由核心逻辑
// 点击跳转按钮时
document.querySelector('#toProOrder').addEventListener('click', (e) => {
e.preventDefault(); // 阻止默认a标签跳转
// 修改 URL 为 /proOrder,不发请求
history.pushState({}, '', '/proOrder');
// 手动渲染对应组件
renderProOrderComponent();
});
// 监听回退/前进
window.addEventListener('popstate', () => {
const path = window.location.pathname;
if (path === '/proOrder') {
renderProOrderComponent();
}
});
⚠️ 关键:pushState 只是修改 URL,不会触发页面刷新,但直接访问 / 刷新 http://xxx.com/proOrder 时,浏览器会向服务器发送该路径的请求------ 这也是你之前部署报 404 的核心原因!
3. Vue Router 中配置 History 模式
typescript
// router/index.ts(你的原有配置)
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [/* 你的路由规则 */];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), // 核心:History 模式
routes
});
export default router;
4. 核心特点(优缺点)
| 优点 | 缺点 |
|---|---|
1. URL 美观 :无 #,符合用户对 URL 的认知习惯;2. SEO 友好 :完整 URL 会被搜索引擎收录,利于网站优化;3. 无锚点冲突 :和页面内原生锚点(如 #top)互不干扰; |
1. 需要后端配置 :直接访问子路由(如 /proOrder)会触发服务器请求,服务器未配置时返回 404;2. 兼容性一般 :仅支持 IE10+ 及现代浏览器;3. 部署成本高:需协调后端修改 Nginx/Apache 配置; |
四、Hash 模式 vs History 模式 核心对比表
| 对比维度 | Hash 模式 | History 模式 |
|---|---|---|
| URL 格式 | http://xxx.com/#/proOrder(带 #) |
http://xxx.com/proOrder(无 #) |
| 底层原理 | 监听 window.onhashchange 事件 |
基于 HTML5 history.pushState/replaceState + popstate 事件 |
| 服务器请求 | # 后的内容不发送到服务器,仅请求 index.html |
完整 URL 发送到服务器,请求 /proOrder 路径 |
| 部署要求 | 无需后端配置,直接部署即可 | 必须配置后端(Nginx/Apache),将所有路由转发到 index.html |
| 兼容性 | 全浏览器兼容(IE6+) | 仅支持 IE10+ 及现代浏览器 |
| SEO 友好性 | 差(爬虫忽略 # 后内容) | 优(完整 URL 被收录) |
| 锚点冲突 | 易和页面内锚点(#top)冲突 | 无冲突 |
| 历史记录 | 基于浏览器哈希历史,自动记录 | 基于 HTML5 History API,可自定义历史记录 |
五、实战部署:History 模式解决 404 的核心方案
部署 History 模式报 404,本质是 Nginx 未配置「路由转发」,以下是不同后端的配置方案:
1. Nginx 配置(最常用,你的场景)
nginx
server {
listen 80;
server_name 你的域名/服务器IP;
root /usr/share/nginx/html/你的项目dist目录; # 项目打包后的根目录
index index.html index.htm;
# 核心:所有请求转发到 index.html,由前端路由接管
location / {
try_files $uri $uri/ /index.html; # 先找物理文件→找不到就转发到index.html
add_header Cache-Control "no-cache, no-store, must-revalidate"; # 避免缓存
}
# 静态资源缓存(可选,提升加载速度)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
}
2. Apache 配置
修改 .htaccess 文件(放在项目根目录):
apache
<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 配置
javascript
const express = require('express');
const path = require('path');
const app = express();
// 静态文件目录
app.use(express.static(path.join(__dirname, 'dist')));
// 所有路由转发到 index.html
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
app.listen(3000, () => {
console.log('服务器运行在 3000 端口');
});
六、选型建议:什么时候用 Hash/History?
选 Hash 模式的场景
- 内部管理系统(如你的工单系统)、无需 SEO 的项目;
- 快速开发、无法协调后端配置服务器;
- 需要兼容低版本浏览器(如 IE8 及以下);
- 临时部署、测试环境(无需复杂配置)。
选 History 模式的场景
- 对外展示的官网、需要 SEO 优化的项目;
- 追求 URL 美观,提升用户体验;
- 团队能协调后端修改 Nginx/Apache 配置;
- 现代浏览器环境(无需兼容低版本 IE)。
七、常见问题 & 解决方案
1. History 模式刷新 404
- 原因:服务器未配置路由转发,直接请求
/proOrder时找不到对应文件; - 解决:按上文配置 Nginx/Apache,添加
try_files(Nginx)或 Rewrite 规则(Apache)。
2. Hash 模式 URL 中的 # 影响锚点
-
问题:页面内
<a href="#top">回到顶部</a>会被路由拦截; -
解决:Vue Router 可配置
hashPrefix自定义哈希前缀(如#!):typescriptconst router = createRouter({ history: createWebHashHistory({ hashPrefix: '!' }), // 哈希前缀改为 #! routes });此时 URL 为
http://xxx.com/#!/proOrder,原生锚点#top不会冲突。
3. History 模式部署在子路径(如 /vue3-app)
- 问题:部署在
http://xxx.com/vue3-app/proOrder时路由失效; - 解决:
- Vite 配置
base: '/vue3-app/'; - 路由配置
createWebHistory(import.meta.env.BASE_URL); - Nginx 配置
root /usr/share/nginx/html/vue3-app;+try_files $uri $uri/ /vue3-app/index.html;。
- Vite 配置
总结(核心关键点)
- Hash 模式 :靠
#分隔,无需后端配置,兼容性好,URL 带 #,SEO 差; - History 模式:基于 HTML5 API,URL 美观,SEO 好,但需后端配置路由转发,否则 404;
- 选型核心:对内系统 / 快速开发用 Hash,对外网站 / 需 SEO 用 History;
- 你的项目(工单系统):若无需 SEO,用 Hash 模式可避免 Nginx 配置;若追求 URL 美观,按上文配置 Nginx 即可解决 404。