正确做法是将 hashchange 事件监听器绑定到 window 对象,使用 window.addeventlistener('hashchange', handler) 并在 domcontentloaded 时注册;解析 location.hash 需用 .slice(1) 提取路径,首次加载需手动触发路由;避免 a 标签 href 跳转,改用 js 控制 navigate。

不要用 body.onhashchange,直接挂 window 上
浏览器不支持在 上设置 onhashchange 属性——它只存在于 window 对象上。写成 完全无效,事件根本不会触发。
正确做法是用 window.addEventListener('hashchange', handler),且必须在 DOM 加载完成后绑定,否则可能因元素未就绪而漏掉事件。
- 别写
window.onhashchange = handler:容易被后续代码覆盖,尤其引入第三方脚本时不可靠 - 绑定时机选
DOMContentLoaded而非load:更早执行,减少白屏时间 - handler 必须是具名函数或稳定引用:避免箭头函数导致无法移除(虽一般不需要移除)
location.hash 解析必须 .slice(1),不能用 .substr(0, 1) 或漏默认值
location.hash 返回的是带 # 的完整字符串,比如 "#/user/123"。直接拿它去查路由表会永远匹配不上。
常见错误是写成 location.hash.substr(0, 1)(这取的是 "#"),或者忘记处理空 hash 场景(如打开 https://site.com/ 时 location.hash === "")。
- 统一用
const path = location.hash.slice(1) || '/'提取路径,简洁安全 - 如果要支持带查询参数的哈希(如
#/post?id=123),再用new URLSearchParams(path.split('?')[1])拆解,但注意 IE 不支持需 polyfill - 手动改 hash 时别加
#:location.hash = '/about'即可,浏览器自动补
首次加载必须手动触发一次路由,否则页面白屏
用户直接访问 https://site.com/#/profile 时,hashchange 事件完全不触发——浏览器认为这是初始状态,不是“变化”。结果就是 DOM 一直空着,直到用户点一次链接才开始渲染。
这个坑在本地开发常被忽略,因为习惯性从首页点进去;但真实场景中分享链接、刷新、微信内嵌页都会暴露问题。
- 在
DOMContentLoaded回调里立即调用一次你的路由处理函数,比如route()或onHashChange() - 不要只检查
location.hash是否为空再执行:即使有 hash,也得走一遍匹配逻辑 - 守卫逻辑(如只响应
/xxx开头的哈希)必须放在解析之后、匹配之前,否则非法 hash 也会进路由表查找
跳转别用 a 标签 href,防止意外滚动和锚点冲突
写 @#@#@#@#@#@#@#@#@#@0 看似方便,但有两个硬伤:
- 点击后浏览器会尝试滚动到页面内 id="home" 的元素(如果存在),导致视图错位
- 若页面有
<div id="home">,用户点这个链接实际触发的是原生锚点跳转,<code>hashchange虽然也触发,但中间夹杂了 DOM 滚动,体验割裂 - 更隐蔽的问题:手敲地址栏改 hash 后回车,
hashchange仍触发,但你的 handler 若没做幂等处理(比如重复绑定事件、重复初始化状态),就会出 bug
推荐方案是全部用 JS 控制:@#@#@#@#@#@#@#@#@#@1,配合 function navigate(path) { location.hash = path; }。干净、可控、无副作用。











