天天看點

一個來自create-react-app腳手架警告的思考

最近在開發一個react項目,項目是用create-react-app腳手架建立的,當我在我的項目的菜單欄中添加了一個打開一個外鍊的a标簽時,我收到了一個來自create-react-app的警告資訊,資訊内容如下

意思就是說“在沒有rel="noopener noreferrer"屬性的a标簽中使用target="_blank"存在一定的風險”

這個提示瞬間把我吸引了,以前關于a标簽收到的提示都是沒有設定alt屬性啊什麼的,但是也隻是提示我說為了顯示的友好什麼的,這次竟然提示我有風險,面對這種問題,必須一探究竟啊。

查閱了一些資料得到了如下關于a标簽一個介紹

當一個外部連結使用了target=_blank的方式,這個外部連結會打開一個新的浏覽器tab。此時,新頁面會打開,并且和原始頁面占用同一個程序。這也意味着,如果這個新頁面有任何性能上的問題,比如有一個很高的加載時間,這也将會影響到原始頁面的表現。如果你打開的是一個同域的頁面,那麼你将可以在新頁面通路到原始頁面的所有内容,包括document對象(window.opener.document)。如果你打開的是一個跨域的頁面,你雖然無法通路到document,但是你依然可以通路到location對象。

不看不知道一看吓一跳有木有。主要是兩個點是我以前從未在意的

  1. 用target="_blank"方式打開的tab和原始頁面占用同一個程序(UI程序)
  2. 新打開的頁面能擷取到原始頁面的document。

第一個問題不用我說都知道是非常需要注意的,新的頁面中的所有行為都會間接影響到原始頁面的性能。

這裡主要研究第二個問題。為此,我做了小小的實驗。

上圖解釋:

  • 首先打開了第一個頁面,第一個頁面隻有一個“打開一個新頁面”的a标簽
  • 點選這個連結,打開了一個新頁面。新頁面中有一個按鈕,“告訴打開我的那個頁面,我喜歡林志玲”。
  • 點選新頁面的按鈕然後回到第一個頁面,發現第一個頁面多出來了一排紅色的文字“我喜歡林志玲”。
  • 停在第一個頁面5s鐘,第一個頁面自動跳轉到了百度首頁。

上面兩個頁面的代碼分别如下:

opener-test.html

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
    <body>
        <a target="_blank" href="test-opener-2.html">打開一個新頁面</a>
    </body>
</html>           

複制

opener-test-2.html

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
    <body>
        <h1>我是新打開的頁面</h1>
        <button type="button" id="btn">告訴打開我的那個頁面,我喜歡林志玲</button>
        <script>
            document.getElementById('btn').addEventListener('click', function() {
                var _opener = window.opener;
                var p = _opener.document.createElement('p');
                p.innerHTML = "我喜歡林志玲";
                p.style.color = "#f33";
                _opener.document.body.appendChild(p);
                setTimeout(function() {
                    _opener.location.href = "//www.baidu.com";
                }, 5000)
            });
        </script>
    </body>
</html>           

複制

新的頁面不僅往原始頁面添加了一段話,而且還讓他離開了原來的頁面。

注:在上面的例子中,兩個頁面位于同一個域下面,如果兩個頁面位于不同的域,那上面的第一個效果就是不行的,因為不同域的情況下,新頁面拿不到opener對象的document,但是location對象是可以拿到的,是以第二個效果任然有效。

怎麼禁止上面的行為呢?按照create-react-app的提示資訊,給連接配接加上rel屬性,如下:

<a rel="noopener noreferrer" target="_blank" href="https://marvengong.github.io/fastmock-docs/book/">           

複制

上面的rel屬性值多了一個noreferrer它的作用和noopener是一樣的,隻是适用于低版本的浏覽器。

這樣處理後,新打開的頁面的window對象上就沒有opener和referrer對象了。