Nginx 配置 Vue 项目 Hash/History 模式路由跳转错误的解决方案
大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。
图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG
我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。
展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!
文章目录
- 前言
- 背景:Nginx 配置 + Vue 路由模式
- 遇到的问题
- 问题出在哪里?
- 如何解决?
- 方案一:继续使用 Hash 模式(推荐初学者)
- 方案二:使用 History 模式(推荐最终方案)
- 实战 Demo 配置(可运行)
- 总结
前言
最近在一个实际项目中,前端采用 Vue2,路由模式是 hash
,后台通过 Nginx 部署。我们遇到一个让人头疼的问题:访问子路径路由 /inv/#/login
时,竟然跳转到了错误的页面。切换成 history
模式后路由倒是正常了,但一刷新页面,前端资源就加载失败,提示 MIME 类型错误。
这篇文章我会把问题的原因分析清楚,然后结合 Nginx 配置与 Vue 路由的特性给出可运行的解决方案,顺便给出实际案例中的 Demo 配置,方便你参考。
背景:Nginx 配置 + Vue 路由模式
在我们的项目中,存在两个前端子系统:
- 管理端:路径
/
→ 对应目录/data/web/sccmp/
- 组长端:路径
/inv/
→ 对应目录/data/web/inv/
Nginx 配置如下:
location / {alias /data/web/sccmp/;index index.html index.htm;try_files $uri $uri/ /index.html;
}location /inv/ {alias /data/web/inv/;index index.html index.htm;try_files $uri $uri/ /inv/index.html;
}
Vue 项目中,路由配置采用的是 hash
模式:
const router = new VueRouter({mode: 'hash',routes: [{ path: '/login', component: Login },{ path: '/dashboard', component: Dashboard }]
})
遇到的问题
-
在 hash 模式下访问组长端
https://xxxx.xx.cppinfo.cn/inv/#/login
页面居然跳转到管理端的登录页,而不是组长端。 -
在 history 模式下刷新页面
出现错误:Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/html".
简单说就是 JS 资源请求到了 HTML 文件,Nginx 没有正确返回对应的静态资源。
问题出在哪里?
这个问题本质上分两层:
-
Hash 模式下路径混淆
Hash 模式的 URL 是https://domain/inv/#/login
,实际上 Nginx 并不知道#/login
部分,它只处理/inv/
,而 Vue 的 JS 脚本却从/
路径下去加载资源,导致加载的是管理端的 index.html。 -
History 模式下资源路径错误
在 history 模式下,Vue 的 URL 例如/inv/login
,Nginx 需要把所有未知路径回退到/inv/index.html
。如果配置不对,就会把资源请求(比如/inv/js/app.js
)错误地 fallback 到 HTML,从而导致MIME type
错误。
如何解决?
方案一:继续使用 Hash 模式(推荐初学者)
如果项目允许继续用 hash 模式,需要确保 每个子系统的资源路径是独立的,避免加载错目录。
在 Vue 的 vue.config.js
中设置 publicPath
:
// vue.config.js
module.exports = {publicPath: '/inv/', // 对应组长端的部署路径outputDir: 'dist',productionSourceMap: false
}
这样构建出来的资源都会挂在 /inv/
下,避免冲突。
Nginx 保持原配置即可。
方案二:使用 History 模式(推荐最终方案)
History 模式对 URL 更友好,但需要 Nginx 配置精确区分资源和路由。
Nginx 修改如下:
location / {root /data/web/sccmp;index index.html;try_files $uri $uri/ /index.html;
}location /inv/ {root /data/web/inv;index index.html;# 只针对 html 路由回退try_files $uri $uri/ /index.html;
}
关键点:
root
用而不是alias
(如果用 alias,要小心路径拼接规则)。try_files
中/index.html
会 fallback 到当前root
的 index.html,而不会错误地去加载/
下的文件。- 保证静态资源路径正确,例如
/inv/js/app.js
请求到/data/web/inv/js/app.js
。
实战 Demo 配置(可运行)
假设我们有两个 Vue 项目,构建产物分别在 /data/web/sccmp/
和 /data/web/inv/
。
Vue 配置 vue.config.js
:
// 管理端 vue.config.js
module.exports = {publicPath: '/',outputDir: 'dist-admin',
}// 组长端 vue.config.js
module.exports = {publicPath: '/inv/',outputDir: 'dist-inv',
}
Nginx 配置:
server {listen 80;server_name demo.local;location / {root /data/web/sccmp;index index.html;try_files $uri $uri/ /index.html;}location /inv/ {root /data/web/inv;index index.html;try_files $uri $uri/ /index.html;}
}
这样:
- 访问
http://demo.local/
→ 管理端 - 访问
http://demo.local/inv/
→ 组长端 - 刷新任意路由(无论是
/dashboard
还是/inv/login
)都能正常返回对应的 index.html,再由前端接管路由。
总结
- Hash 模式:简单,兼容性强,但 URL 不够美观。需要在 Vue 构建配置里设置好
publicPath
,避免资源路径错乱。 - History 模式:更优雅,但 Nginx 必须配合
try_files
做路由回退,确保静态资源不会被错误地 fallback 到 index.html。 - 问题本质:不是 Vue 路由写错,而是 Nginx 路径处理和前端
publicPath
没有对应上。
我的建议是:开发初期可以用 Hash 模式省心,上线前建议切换到 History 模式,并仔细配置好 Nginx。