開發項目過程中,會遇到點擊頁面上某個東西,要在新窗口中打開一個頁面的需求。如果你認為是簡簡單單的打開一個新窗口,那就會掉進谷歌瀏覽器的坑里了。
場景:在A頁面,點擊按鈕,新開窗口跳轉到該項目的B頁面,如果B頁面的請求尚未結束,直接關閉B頁面,此時A頁面會卡死。
原因:谷歌瀏覽器在給新開窗口分配進程時,看是否同源,非同源頁面會單獨分配一個新進程,而同源頁面只會分配一個相同的新進程。在上述場景中,A,B頁面同源,所以谷歌瀏覽器只會分配一個進程,這就導致B頁面在請求未結束時就關閉,A頁面會卡死。
下面來看一下不同方式打開新頁面的瀏覽器分配進程情況。
一、查看谷歌瀏覽器的進程
1、找到瀏覽器的自定義及控制圖標
2、點擊找到更多工具
3、點擊更多工具並點擊任務管理器
4、進程如下:
二、使用a標簽打開新窗口
1、使用a標簽打開非同源窗口
<a href="https://www.baidu.com/" target="_blank">新開窗口跳轉到百度</a> <a href="https://www.cnblogs.com/" target="_blank">新開窗口跳轉到博客園</a>
使用a標簽分別打開了百度以及博客園首頁,這兩個非同源,所以瀏覽器會分配兩個新進程。如下圖:
2、使用a標簽打開同源窗口
<a href="https://juejin.im/" target="_blank">新開窗口掘金</a> <a href="https://juejin.im/" target="_blank">新開窗口掘金</a>
使用a標簽打開掘金首頁,這兩個同源,所以瀏覽器只會分配一個新進程。如下圖:
三、使用window.open打開新窗口
使用window.open打開新窗口與a標簽打開結果相同
<Button onClick={() => window.open('https://www.csdn.net/')}>window.open跳轉到CSDN</Button> <Button onClick={() => window.open('https://www.csdn.net/')}>window.open跳轉到CSDN(同源)</Button> <Button onClick={() => window.open('https://www.jianshu.com/')}>window.open跳轉到簡書(非同源)</Button>
點擊3個Button,打開3個新窗口,只分配了2個進程,CSND同源,共用一個進程,簡書單獨一個進程。
四、如何解決同源同一進程
一般來說,新開窗口肯定是想新開一個進程,這樣兩邊頁面不會受到影響。要想同源不同進程,就需要將新創建的標簽頁的 opener 屬性設置為 null,表示在單獨的進程中運行新標簽頁,這樣瀏覽器就會給每一個新開窗口分配一個進程。
1、window.open設置opener為null不會起作用
const handleClick = () => { let otherWindow: any = window.open(); otherWindow.opener = null; otherWindow.location = 'https://www.baidu.com/'; } <Button onClick={handleClick}>window.open設置opener跳轉到百度</Button> <Button onClick={handleClick}>window.open設置opener跳轉到百度</Button>
將opener設置為null之后,再打開新窗口。結果瀏覽器並沒有單獨分配進程,還是同源分配一個進程。
這就要使用a標簽代替window.open來解決
2、a標簽添加rel="noopener noreferrer"解決同源同進程
<a href="https://www.baidu.com/" target="_blank" rel="noopener noreferrer">新開窗口添加rel跳轉到百度</a> <a href="https://www.baidu.com/" target="_blank" rel="noopener noreferrer">新開窗口添加rel跳
轉到百度</a>
a標簽添加rel="noopener noreferrer"就相當於將新窗口的opener置為null,此時瀏覽器會給新開窗口都單獨分配一個進程。
五、項目中使用
創建a標簽並觸發方法打開新頁面
let a = document.createElement('a'); //創建a標簽 a.setAttribute('href', url); //url即為需要打開的新頁面的url a.setAttribute('target', '_blank'); //_blank新窗口打開 a.setAttribute('rel', 'noopener noreferrer'); //添加rel document.body.appendChild(a); a.click(); //觸發click
這樣每次打開一個新窗口時,就會創建一個新的進程。