如何解决异步新窗口被拦截的问题
在异步操作的回调中通过新窗口打开页面,通常会被浏览器会拦截。这种情况有一个比较好的方法可以规避。
# 问题描述
曾遇到过一个问题。在 ajax 请求回调中使用 window.open 新窗口打开页面的时候,有很大概率被浏览器拦截。当时用的是 360 浏览器,以为是 360 作妖。后来发现在 chrome 上也同样会出现。
如果不是异步操作,都是能正常打开,完全不存在被拦截的情况。
以下代码可以很容易复现这种情况。
setTimeout(()=>{
window.open('https://zcc.ren')
},5000)
1
2
3
2
3
# 原因分析
猜测是浏览器认为,异步操作中新窗口弹出的页面不是用户直接触发或主动打开的,所以将其视为广告或者恶意网站之类的,自动拦截。
# 解决思路
既然找到原因了,知道是因为异步操作导致的,那就想办法让它变成同步的操作。
试了几个投机取巧的方法,试图绕开浏览器的这个机制
- 把新窗口打开页面的操作绑定在一个按钮的点击事件上,请求回调后触发这个按钮的点击事件。
- 把新窗口页面的地址绑定在设置了 target="_blank" 的 a 标签上,请求回调后触发这个链接的点击事件。
- 把新窗口页面的地址绑定在设置了 target="_blank" 的表单上,请求回调后提交这个表单。
以上的方法虽然很不优雅,但看起来好像能行。然而还是太年轻了,事实上,这些方法都不行,浏览器还是知道这是异步操作,依然会拦截。
最后想到一个可行又优雅的方法。就是请求前先把新页面打开(同步操作,不会被拦截),请求完成后,再让这个页面跳转到真正的目标页面。
# 解决方法
// 先直接打开跳转页,同步不会被拦截,不设置具体地址,会打开一个 about:blank
const newWindow = window.open()
// 异步操作开始
axios('/xxx')
.then(()=>{
// 需要异步打开新页面的情况下,url通常都是跟回调数据有关的,这里可以自定义
newWindow.location.href='https://zcc.ren'
})
.catch(()=>{
// 错误时关闭页面
newWindow.close()
})
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
请求前先打开了一个空白页。等到异步操作完毕之后,如果成功就让这个页面跳转到具体的目标地址,如果失败就将这个页面关闭。
如果等待时间较长,空白页面会呈现比较久。可以考虑新建一个 html 页面来显示 loading。将第一步打开页面的操作指定到这个 loading 的页面。
这样规避了异步新窗口打开页面的操作,不会被拦截,个人认为是比较完美的解决方案。
上次更新: 2022/07/27, 15:36:28