哈希路由刷新404是因服务端未配置spa回退,需在nginx中添加try_files $uri $uri/ /index.html;history.pushstate后popstate不触发多因监听时机错误或iframe干扰;应优先使用history模式而非hash模式,仅在静态托管等受限场景才用哈希。

哈希路由刷新后404,是因为服务端没配静态回退
浏览器访问 https://site.com/#/user 时,# 及之后的内容根本不会发给服务器,所以服务端看到的永远是 /。但用户手动刷新或直接访问带哈希的 URL,如果服务端配置的是严格路径匹配(比如 Nginx 默认不处理哈希),就可能返回 404——这不是前端问题,是服务端没把所有路径都指向 index.html。
常见错误现象:Failed to load resource: the server responded with a status of 404 (),但控制台没报 JS 错误,页面白屏。
- Nginx 需加
try_files $uri $uri/ /index.html;到 location 块中 - Vercel/Netlify 等平台默认支持 SPA 回退,无需额外配置
- 本地用
serve -s build(来自serve包)比直接双击打开index.html更可靠,后者不支持哈希路由刷新
history.pushState 不触发 popstate?检查事件监听时机和 iframe 干扰
用 history.pushState 改变 URL 后,用户点浏览器后退按钮,本该触发 popstate 事件来驱动路由跳转,但有时完全没反应。最常见原因是:监听器在路由系统初始化前就被移除了,或者被第三方脚本(尤其是含 iframe 的广告/埋点 SDK)劫持了历史栈。
- 确保
window.addEventListener('popstate', handler)在应用启动时执行,且 handler 不被重复绑定或意外解绑 - 避免在
iframe内调用history.pushState,某些老版本 iOS Safari 会静默失败且不抛错 - 调试时可在控制台手动执行
history.back(),看是否触发popstate,排除用户操作问题
Vue Router / React Router 默认哈希模式,但你其实不需要它
很多人一上来就用 createWebHashHistory() 或 HashRouter,觉得“兼容性好”,但现代项目基本不需要。哈希路由有硬伤:URL 不美观、SEO 友好度低、服务端无法做路径级缓存或权限控制、scrollRestoration 行为异常。
- 只要服务端支持 SPA 回退(见第一个副标题),就该优先用
createWebHistory()或BrowserRouter - Webpack/Vite 开发服务器默认已配好 historyApiFallback,开发时不用哈希也能正常刷新
- 唯一必须用哈希的场景:部署在纯静态托管(如 GitHub Pages 默认不支持自定义 404 重定向)、或嵌入到不控源的服务(如某些 CMS 的 iframe 插件页)
location.hash 手动更新不触发 Vue/React 响应式更新
直接赋值 location.hash = '#/order' 能改地址栏,但 Vue 的 watch $route 或 React 的 useLocation 不会响应——因为哈希变化没走路由系统的 push 方法,只是原生 DOM 层面变更。
- 永远通过路由实例的方法跳转:
router.push('/order')或navigate('/order'),而不是操作location.hash - 如果必须监听原生哈希变化(比如接老系统),需手动
addEventListener('hashchange', ...),并注意与框架路由逻辑冲突 -
hashchange事件在 Safari 中可能延迟触发,尤其配合setTimeout改 hash 时,建议用requestAnimationFrame包一层
try_files,结果所有深层路由刷新全 404。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











