前言
一个简单的优化需求,竟然引发了window.open()的奇妙化学反应☠️
背景
项目X的A页面需要点击一个区域后,跳转到对应的页面B,这个页面需要新开窗口来展示。B页面成功打开后再起接口还在loading的时候关闭,会造成当前浏览器中所有项目X的页面卡死;反之当B页面所有接口loading完成后再关闭就不会卡死😅。这里跳转尝试了多种方式——window.open();React的<Link />标签;react-router的useHistory等等,除了useHistory正常外其他均以卡死阵亡,但是使用useHistory的跳转效果又不符合产品想要的新窗口展示😱。
调研
经过不限的努力去baidu~😮💨终于找到它的关键——进程!😱
进程模型
站点(site):站点的判定规则相当于不严格的同源协议,详见第二篇参考。
站点实例(site instance):站点实例是来自同一站点且有关联关系的页面的集合,详见第二篇参考。
以Chrome浏览器为例,Chrome在4.0.229.1版本的时候引入了多进程架构(现在已经是96.0.4664.55版本了😅),其支持了下面四种进程模型:
[Default]Process-per-site-instance:此模型下Chrome会为每个site instance创建一个渲染进程,来确保不同site instance被独立呈现。如果A、B两个页面在脚本中存在引用,如在http://www.projectX.com/A.html使用JS打开一个新窗口http://www.projectX.com/B.html🎯,浏览器会认为这两个页面存在关联关系,并将其划入同一个site instance。
Process-per-site:此模型以site维度来为分组,同一site的页面使用一个渲染进程。使用方式为Chromium启动时注入--process-per-site命令来启动。
Process-per-tab:此模型每一个选项卡都有一个专属渲染进程,不论页面有没有联系,且不随时间而改变。使用方式为Chromium启动时注入--process-per-tab命令来启动。
Single process:人如其名,此模型是启用单进程,所有选项卡共用一个进程。使用方式为Chromium启动时注入--single-process命令来启动。
原因
了解完进程模型后就大致分析出问题所在了。跳转的落地页需要拉取大量数据用于渲染图表,存在高开销的JS执行内容,提前关闭窗口导致JS某处出错/产生死循环(🧐推测的),从而使整个进程卡死。
下面用个例子在佐证一下我的推测:
A通过onclick事件触发window.open事件,跳转到B页面;B页面有死循环
疯狂关闭B页面
回到A页面,A页面也被卡死
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page A</title>
</head>
<body>
This is Page A !
<button id="grave">TO B</button>
<script>
const buttonDom = document.getElementById('grave');
buttonDom.onclick = () => {
window.open('./indexB.html');
}
</script>
</body>
</html>