location.hash 能做路由是因为修改它不刷新页面、触发hashchange事件、存入历史栈且服务端不可见;需监听load和hashchange事件并手动解析hash值。

location.hash 支持前端路由,而且是最早、最轻量、兼容性最强的实现方式之一。它不需要服务端配合,也不依赖 HTML5 新特性,IE8 都能跑。
为什么 location.hash 能做路由?
浏览器对 URL 中 # 后面的部分(即 hash)有特殊处理:
- 修改 window.location.hash 不触发页面刷新
- 每次 hash 变化都会触发 hashchange 事件
- 浏览器会把每次 hash 变化记入历史栈,支持前进/后退
- 服务端完全收不到 hash 部分,所以无需 Nginx 或后端路由配置
常见错误现象:
- 直接改
location.href = 'xxx#about'而不是location.hash = 'about',导致整页跳转 - 忘了在页面加载时手动执行一次路由匹配(
load事件里没调refresh()),导致首次访问#about页面不渲染 - 使用
addEventListener('hashchange', handler)但没用.bind(this)或箭头函数,导致this指向丢失
无序列表说明关键点:
-
hash值始终是字符串,包括开头的#,实际取值要用location.hash.slice(1) -
hashchange事件不冒泡,也不能取消,默认行为就是更新地址栏 - 如果 URL 是
@#@#@#@#@#@#@#@#@#@0,location.hash拿到的是#user/123,不是user/123 - 单页应用中,
history.pushState和location.hash不能混用,否则历史栈会混乱
hashchange 事件监听要注意什么?
这个事件看似简单,但几个细节不注意就会漏掉首次加载或状态不同步:
- 它只在 hash 真正变化时触发,页面首次加载带 hash 不会自动触发(必须手动模拟一次)
- 它不区分是 JS 修改还是用户点击链接引起的变更,统一响应
- 在 iframe 里修改父页面 hash,父页面的 hashchange 仍会触发
推荐写法(兼顾首次加载和后续变更):
function onHashChange() {
const path = location.hash.slice(1) || '/';
renderView(path);
}
<p>window.addEventListener('load', onHashChange);
window.addEventListener('hashchange', onHashChange);</p>
不要只监听 hashchange,漏掉 load 就会导致用户直接访问 @#@#@#@#@#@#@#@#@#@1 时页面空白。
Hash 模式 vs History 模式:什么时候该选 hash?
不是“哪个更高级就选哪个”,而是看部署环境和兼容需求:
- 静态托管(如 GitHub Pages、Netlify、Vercel)没后端路由能力 → 必须用 hash 模式
- 项目要支持 IE9 或更低版本 → 只能用 hash 模式
- 开发阶段想快速验证路由逻辑,不想配 Nginx rewrite 规则 → hash 更省事
- 你用的是 create-react-app 或 vue-cli 默认模板,且没改 vueRouter 的 mode → 默认就是 hash
容易踩的坑:
- 把 hash 路由当成“临时方案”随便写,结果上线后发现 SEO 友好性差、分享链接带
#显得不专业 - 在 history 模式下 fallback 配置出错(比如 Nginx 返回 404 而不是 index.html),又没回退到 hash 模式,导致所有深层路径白屏
- 误以为 hash 模式不能做参数传递——其实
#user?id=123&tab=profile完全合法,只是解析得自己处理search部分
最常被忽略的一点:hash 模式下,location.pathname 始终是原始路径(比如 /index.html),不会随 hash 改变。如果你在代码里混用了 pathname 和 hash 做判断,逻辑很容易错乱。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











