一行 window.location.assign(''),把页面刷新了
一个看起来像"空操作"、实际是"刷新"的 API 陷阱。
起因
代码 review 时看到这样一段:
functiongoNext(){
consturl=getNextUrl()// 某些分支下返回 ''
window.location.assign(url)
}
写这段代码的同学本意是"目标地址还没定,先留空,后面再填"。结果上线后用户点按钮页面就白屏重载一次,SPA 里所有表单内容当场蒸发。
问题就出在:assign('') 里的空字符串,不是"什么都不做"。
结论先行
window.location.assign('') 会导航到当前 URL —— 效果约等于刷新页面。
因为 assign() 接收的参数会按照当前文档的 URL 做相对解析,空字符串解析出来的结果,就是当前页面自身的完整地址。
// 当前页面 https://example.com/list?page=2
window.location.assign('')
// => 等价于 window.location.assign('https://example.com/list?page=2')
// => 等价于 window.location.href = 'https://example.com/list?page=2'
所以浏览器收到的是一条"请跳转到我当前所在的这个地址"的指令,于是重新发起一次请求、重新解析、重新渲染。
规格层面的解释
Location.prototype.assign(url) 内部走的是 location 的 navigate 流程:
- 把入参
url用当前文档的 base URL 解析成绝对 URL; - 和目标 document 的当前 URL 做比较;
- 决定这次导航是"新导航"、"片段导航"还是"重载"。
空字符串经过第 1 步之后,协议、域名、端口、路径、查询串、哈希全部等于当前 URL,于是进入常规导航 → 页面重载。
一个容易忽略的分支:带 hash 时不刷新
HTML 规范里有一条:如果新 URL 与当前 URL 除片段(fragment)之外完全相同,这次导航会被降级成 fragment navigation——不重新加载文档,只做锚点定位。
所以:
| 当前 URL | assign('') 的实际行为 |
|---|---|
https://a.com/list?page=2(无 hash) |
重新 GET 当前地址 → 整页刷新 |
https://a.com/list?page=2#sec3(有 hash) |
同 URL 同 hash → 不刷新,仅触发滚动 / hashchange |
| 当前页是 POST 表单提交的结果 | 发的是 GET,不会像 reload() 那样弹"确认重新提交" |
同一行代码,在列表页刷新、在锚点页不刷新——这种"看 URL 下菜碟"的行为,是排查时最容易把人绕晕的地方。
和几个"长得像"的写法对比
location.assign('')// 刷新当前页:一次全新的 GET 导航,beforeunload / unload 照常触发
location.reload()// 刷新当前页:保留 POST 语义,可能弹出"确认重新提交表单"
location.href=''// 和 assign('') 完全等价
location.replace('')// 同样刷新,但不新增历史记录,后退键会跳过当前页
location.assign()// TypeError:assign 要求 1 个参数,实际收到 0 个
location.assign(undefined)// 拼成相对路径 ".../undefined",直接 404
最后那两条是同一家族里更隐蔽的坑:参数省略直接抛错,参数传 undefined 则会被字符串化成 "undefined"。
真正会咬人的三个场景
1. SPA 里状态全丢
Vue Router / React Router 用 History API 改过地址后,window.location 依然是当前真实 URL。assign('') 触发的是浏览器级整页重载,不是路由跳转:
- Pinia / Redux / Vuex 里的内存状态清空;
- 组件内的表单草稿、滚动位置、弹窗层级全部重置;
- 已建立的 WebSocket / SSE 连接断开重连。
想要"重新加载"就用 location.reload(),语义直白;想要路由级刷新,应该走路由实例(router.go(0) 或者干脆重新拉取数据),别碰 location。
2. 条件分支里的空值兜底
最常见的出错形态:
consttarget=data.redirectUrl||''
window.location.assign(target)// data.redirectUrl 为空时静默刷新
// 正确写法:先判空,空值就是明确的"不跳转"
consttarget=data.redirectUrl
if(target){
window.location.assign(target)
}
记住一条规则:跳转动作不应该有"默认行为"。地址为空就什么都不做,这才符合直觉。
3. 循环刷新
如果 assign('') 写在页面初始化逻辑里(比如某个鉴权失败的兜底分支),刷新后再次执行、再次刷新,浏览器会打出一片 net::ERR_TOO_MANY_REDIRECTS 或者直接循环白屏。这类 bug 在线上很难复现,因为本地开发常常因为缓存而表现不同。
怎么验证
在控制台跑一遍最直观(注意会刷新页面):
// 1. 先给当前页做个标记
sessionStorage.setItem('__reload_probe',String(Date.now()))
constbefore=sessionStorage.getItem('__reload_probe')
// 2. 触发争议语句
window.location.assign('')
// 3. 页面重载后再看标记还在不在(sessionStorage 跨刷新保留)
或者用 Performance 面板 / Network 面板看:执行后是否多出一次 document 请求。带 hash 的页面可以监听 hashchange 观察它只走片段导航。
一句话总结
assign('') 不是空操作,是刷新。
- 想刷新 → 写
location.reload(),别写assign(''); - 想回首页 → 写
assign('/'),斜杠不能省; - 什么都不想干 → 删掉那一行,或者用
if (url)把空值挡在门外。
前端里这类"参数看起来是空的、实际有默认语义"的 API 还有不少(window.open('') 会开一个空白页、img.src = '' 会重新请求当前页面地址)。看到空字符串参数,先问一句:它到底被解析成了什么。
posted on 2026-09-03 17:26 fox_charon 阅读(0) 评论(0) 收藏 举报
浙公网安备 33010602011771号