这其实是 Vue Router 的 History 模式在"美化 URL"时,与浏览器默认的 HTTP 请求机制产生的一点小冲突。
简单来说,History 模式之所以会"默认变成请求后端",是因为它的 URL 看起来和真实的后端接口路径一模一样,浏览器无法分辨它到底是一个前端页面还是一个后端资源,所以会老老实实地向服务器发起真实的 HTTP 请求。
我们可以通过对比来深入理解:
- 核心原因:URL 的"欺骗性"
* History 模式:URL 是干净的,比如 https://example.com/user/profile。
* 浏览器的逻辑:当你在地址栏输入这个地址并按下回车(或者直接刷新页面)时,浏览器会认为你是在请求服务器上 /user/profile 这个具体的文件或目录。于是,它会向你的后端服务器发起一个真实的 HTTP GET 请求。
* 后端服务器的逻辑:后端服务器(如 Nginx、Apache)接收到请求后,会去自己的文件系统里找有没有 /user/profile 这个文件。显然,你的前端项目打包后通常只有一个 index.html,根本没有这个路径。后端找不到资源,就会直接返回 404 Not Found。
- 对比 Hash 模式:为什么它不会请求后端?
* Hash 模式:URL 会带一个 # 号,比如 https://example.com/#/user/profile。
* 浏览器的逻辑:在 HTTP 协议中,# 及其后面的部分(哈希值)永远只属于前端,不会被包含在发送给服务器的 HTTP 请求中。
* 结果:当你访问 Hash 模式的 URL 时,浏览器实际发给后端的请求只有 https://example.com/。服务器只需返回 index.html,剩下的 #/user/profile 交给浏览器加载完 Vue 应用后,由前端的 Vue Router 自己去解析和渲染。因此,Hash 模式天然不需要后端做任何特殊配置。
- 为什么在页面内点击跳转时,History 模式又不请求后端?
这是很多小伙伴容易混淆的地方。
* 页面内跳转(如 router.push('/about')):这是由 Vue Router 拦截并调用了浏览器原生的 history.pushState() API。这个 API 的作用就是在不刷新页面、不向后端发请求的前提下,单纯地修改浏览器地址栏的 URL。所以,在单页应用内部点击跳转时,一切都是前端在"自导自演",后端完全不知情。
* 刷新或直接访问:一旦你按下了 F5 刷新,或者把 /about 这个链接发给朋友让他直接打开,浏览器就会重新发起一次完整的、真实的 HTTP 请求,这就回到了第 1 点提到的 404 问题。
💡 如何解决这个问题?
解决这个问题的核心思路就是"兜底":你需要配置后端服务器(Nginx、Apache 等),告诉它:"除了那些真实的静态资源(如 .js、.css、图片等)和真实的后端 API 接口外,任何找不到的路径,都请统一返回前端的 index.html"。
这样,当浏览器请求 /user/profile 时,后端虽然找不到这个文件,但会把 index.html 返回给浏览器。浏览器加载了 Vue 应用后,Vue Router 就会根据当前的 URL (/user/profile) 自动匹配并渲染出正确的页面。
以最常见的 Nginx 为例,只需要在配置文件中加上这样一行 try_files 即可:
location / {
try_files uri uri/ /index.html;
}
这行代码的意思就是:先尝试找具体的文件(uri),再尝试找目录(uri/),如果都找不到,就乖乖地把 /index.html 返回回去。