本文将从history模式的核心原理出发,拆解404错误的产生原因。
一、history模式到底是什么?
前端路由的本质,是在不刷新整个页面的前提下,实现页面内容的切换——本质是“客户端路由”,由前端框架(Vue、React等)接管路由逻辑,而非依赖后端服务器。
history模式基于浏览器的 History API 实现,主要依赖两个核心方法:
1. pushState():新增历史记录
语法:history.pushState(state, title, url)
作用:向浏览器的历史记录中添加一条新记录,同时修改地址栏的URL,但不会向服务器发送任何HTTP请求。
举个例子:在Vue项目中,点击<router-link to="/about">,底层就是调用了pushState(),将URL从 https://xxx.com 改为 https://xxx.com/about,页面内容由Vue路由解析后渲染,全程没有请求服务器。
2. replaceState():替换历史记录
语法:history.replaceState(state, title, url)
作用:和pushState()类似,但它会替换当前的历史记录,而非新增。比如页面跳转时不想留下回退记录(如登录后跳转到首页),就可以用这个方法。
二、history模式为什么会报404?
报404的不是后端接口服务器,而是托管前端静态资源的服务器。
我们先分清两个服务器的职责,避免混淆:
1. 两个服务器的区别
- 「后端接口服务器」:负责提供接口(如 /api/user、/api/list),处理数据查询、业务逻辑,和history模式的404无关。
- 「前端静态服务器」:负责托管前端打包后的静态资源(npm run build 生成的dist文件夹,里面只有index.html、js、css、图片等),常用的有Nginx、Apache、GitHub Pages、Netlify等,404错误就是这个服务器返回的。
2. 404错误的触发逻辑
前端项目打包后,dist文件夹里只有一个入口文件 index.html,没有 about.html、login.html 等物理文件,但history模式的URL会显示 /about、/login,这就导致了冲突:
- 正常前端跳转:用户从首页点击 /about,前端调用pushState()修改URL,不请求服务器,Vue/React渲染对应页面,无404。
- 直接访问/刷新:用户直接在地址栏输入
https://xxx.com/about,或刷新该页面,浏览器会向「前端静态服务器」发送请求,请求路径是 /about。 - 服务器校验:静态服务器会查找自己的目录下,是否有 /about 对应的文件或文件夹(比如 about.html),但dist里只有index.html,找不到就返回404。
补充:本地开发时(npm run dev),webpack/vite自带的开发服务器,已经帮我们处理了这个问题——所有找不到的路径,都会自动返回index.html,所以本地开发永远不会报404,只有线上部署时才会出现。
关键区别:history vs hash
很多人会疑惑,为什么hash模式不会有404问题?核心差异在URL的格式和浏览器的处理逻辑:
- hash模式:URL带#号(如
https://xxx.com/#/about),#后面的内容是“锚点”,浏览器会忽略#后的内容,向服务器请求时,只会发送https://xxx.com,因此不会触发404。 - history模式:URL无#号(如
https://xxx.com/about),浏览器会将整个URL作为请求路径,向服务器发送请求,这也是404问题的根源。
三、解决方案
目前博主还未实践过,暂时不描述。
四、总结
前端路由history模式的核心优势是URL简洁、体验更好,而404问题的本质,是「前端静态服务器」找不到URL对应的物理文件,只需通过配置让服务器兜底返回index.html,就能解决。
- history模式基于浏览器History API,前端跳转不请求服务器,直接修改URL和页面内容;
- 404错误来自前端静态服务器,和后端接口服务器无关;
- 所有解决方案的核心,都是让静态服务器将所有路由请求转发到index.html,由前端框架解析路由。