先简单描述一下需求
需求
- 某个按钮点击之后,弹出登录页弹窗、
- 弹窗中登录注册相关的功能就是已有登录页的功能
- 登录成功之后,刷新当前页面

背景
- 已有登录注册页面
- 登录页和主站不在一个仓库内
于是我开始用 iframe 的方式来实现,弹窗中是一个 iframe,地址是登录页的地址。这样只需要在登录页处理下一个样式自适应的问题即可。
对于实现刷新页面的功能,我的实现方式很简单粗暴,就是在 window 对象上挂一个变量当做命名空间,把 location 放进去。
然后在登录页登录成功的时候做一下判断,如果页面在 iframe 内,就从 window.top上取 location
ts
// 弹窗
window.xxx = {..., location: () => window.location}
// 登录页
if(inFrame){
window.top.xxx.location().reload()
}else{
window.location.xxxxx()
}
这个需求就做完,并且在测试环境自测没有问题,就提测了。
提测之后 QA 提了一个 bug,"在海外beta环境,页面不会在当前页刷新,而是会跳转到首页。"
这句话有几个重要的信息
- 海外环境
- beta(正式环境配置)
- 会跳转到首页
由于我们的项目在不同环境是同一份产物,只是配置有区别,所以我就跳过了前两点,直接主攻第三点。
查看开发者工具网络面板过滤仅查看 document,发现当前页面确实执行了刷新,但是被 cancel 了,然后跳转去了首页

由于是正式环境编译,调用栈根本没法看,于是我只能在代码里肉身搜索写了跳转主页的地方,好在有 ts 枚举,我查了半个小时,发现一处可疑代码。大概是这样写的。
ts
const channel = new BroadcastChannel('user-login')
channel?.addEventListener('message', () => {
setTimeout(() => {
location.replace('/home')
}, 500)
})
问了一下相关同事,了解到这是一个需求,当用户在一个 tab 内执行了登录操作,其他打开的 tab 都要自动跳转到首页。
于是问题产生的原因就很清晰了,就是因为和这个需求冲突了,因为 iframe 也算一个新的 tab,当我在弹窗中登录成功之后
- iframe 内会调用父级的 location.reload 来刷新页面
- iframe 会向所有 tab 发送广播通知,当其他页面收到消息后 500ms 会导航到首页
问题就是 reload 方法虽然类型是同步的,但是实际上是一个异步操作,后面的函数还是会执行
我写了一个最小化的复现路径
ts
window.location.reload()
setTimeout(() => alert(1),10)
直接把这段代码复制到浏览器中执行,alert是会弹出来的。
分别在 google 和 百度测试结果为
- 百度延迟设置为 100ms,alert 不会弹出来
- google 延迟设置为 150ms,alert 不会弹出来
这也说明了为啥在国内环境没有问题,而在海外环境才会有问题。浏览器页面跳转成功与网络环境有关。
修复
想要最低成本修复这个问题只需要让页面开始卸载的时候就不执行后面的代码即可,那么就可以通过 AbortController
和 beforeunload
事件
ts
const controller = new AbortController('SyncUserBetweenPages')
useEvent('beforeunload', () => {
controller.abort()
})
// ==============================
// 当 signal 被 abort 了就不执行跳转
setTimeout(() => {
if(!controller.signal.aborted){
location.replace('/home')
}
}, 500)
合并代码之后让 QA 回测没有问题,bug 成功修复。
但是事情到这里还没有结束,作为一个偏执狂,我必须要亲眼看到 relaod 是异步的说明,我才能舒服。
于是我开始收集资料
首先上 MDN 看介绍,说了一堆废话,放弃

然后上 ecma 搜搜,意识到这不应该是 js 标准,而应该是浏览器标准
于是再次开始搜索,询问,找到了 html.spec.whatwg.org,在这里面搜一下 location.reload


同样得到的也是一些大话空话。那没办法了,我只能去看源码。
打开 github 去 Webkit 仓库看看,看不懂,并且代码也太多了。我不能因为这个浪费太长时间,所以就靠 ai 帮帮忙。
打开 deepwiki 查看 webkit 仓库,并且提问

效果非常好,没有给我瞎编答案,并且指出代码位置
但是他列出的代码片段信息太少了,我需要结合上下文才能读懂,然后我就想着去仓库对应的文件看一下
结果发现在 github 中搜不到 deepwiki 列出来的文件,因为 deepWiki 生成文档是有版本的,我看的已经过时了。。。
不过好在 github 已经集成了 Copilot,我直接问 Copilot 不就行了吗

根据提示打开文件看一下

看不懂没关系,只需要看最后一行 scheduleRefresh(activeDocument)
,这就可以说明 reload 确实是有时间调度的。
继续深入看scheduleRefresh
是啥

看最后一行调用了 schedule
,去看看实现

重点这两行,已经说明了真的是异步的
让 co-pilot 给我讲讲schedule
是什么意思


至此,我的 debug 过程结束了。