瀏覽器父子窗口間通訊

父子窗口通訊需求背景

最近在完成一個關聯谷歌賬戶的需求。看到網上的大部分觸及前端方面的完成都有這麼個OAuth規範下的流程:

  • 翻開一個子窗口
  • 在子窗口重定向到受權頁
  • 用戶點擊受權按鈕
  • 用戶受權后受權頁會重定向到一個默許的或許用戶自定義的uri。
  • 受權完成

平常在受權頁被受權后子窗口(受權窗口)都邑被封閉。瀏覽器的每一個窗口是制止封閉當前窗口的,只能封閉由當前窗口翻開的其他窗口。有圖有原形:

《瀏覽器父子窗口間通訊》

所以想要封閉子窗口就需要父窗口來操縱。而什麼時刻讓父窗口封閉子窗口就需要子窗口在完成用戶受權后關照父窗口來封閉本身。這就觸及到父子間窗口的通訊。

父子間窗口通訊分兩種狀況

父子窗口同源

瀏覽器的同源戰略還沒相識到就請自行Google了。
根據OAuth流程是不會湧現父子窗口同源的徵象。然則這裏也做一下總結。

父窗口向子窗口通訊

子窗口是由父窗口建立的。父窗口能夠在翻開子窗口后獵取到子窗口的援用,經由過程這個援用能夠觸發子窗口的要領以此向子窗口通報音訊

// parent code
let child_window_handle = null;
$('#open-child-win-btn').on('click', () => {
    child_window_handle = window.open('target_url.html', '_blank', 'width=700, height=500, left=200');
})

這個時刻有一個子窗口的句柄了(handler)。
而子窗口的頁面下有以下要領

// child code
function ProcessParentMsg(msg) {
    // do something with the msg
}

父窗口只需要在挪用子窗口的對應要領就能夠和子窗口完成通訊

// parent code
child_window_handle.ProcessParentMsg('msg_form_parent_window');

子窗口向父窗口通訊

子窗口能夠經由過程window對象的opener屬性訪問到父窗口。而且挪用父窗口的要領來完成向上通訊。

// child code
window.opener.ProcessChildMsg();
// parent code
function ProcessChildMsg(msg) {
    // do something with msg
}

父子窗口同源的狀況下,父窗口是能夠很大水平的掌握子窗口的。除了能夠觸發子窗口的要領,也能夠監聽子窗口的事宜,onbeforeunloadonresize, focus等等, 然則父子窗口不同源的狀況下。父窗口沒法實行子窗口下的要領,也沒法監聽窗口下的事宜。之前想象的封閉子窗口的完成體式格局是在父窗口取得子窗口的句柄然後監聽子窗口的onload,onload以後就挪用父窗口的用於封閉子窗口的要領。明顯這隻能在同源的狀況下發生了。

父子窗口不同源

這類狀況下父子窗口要通訊就需要藉助HTML5的message passing功用了。

父窗口向子窗口通訊

直接看示例😄,
在父窗口中向子窗口派發音訊

// parent window
let child_window_handle = window.open('child_target.html', '_blank', 'width=700, height=500');

child_window_handle.postMessage('Msg to the child window', '*');

在子窗口下監聽音訊

// child window
window.addEventListener('message', (e) => {
    ProcessParentMsg(e.data);
});

function ProcessParentMsg(msg) {
    // do something with the msg
}

子窗口向父窗口通訊

// child window
window.opener.postMessage("Message to parent", "*");
// parent window
window.addEventListener('message', function(e) {
    ProcessChildMsg(e.data);
}, false);

function processChildMsg() {
    //  do something with the message
}

總結

當我在完成點擊按鈕翻開受權窗口的時刻一向湧現窗口被阻攔的提醒,沒法直接翻開受權彈窗口。這是由於點擊window.open這個操縱是在異步操縱的回調內里實行的。默許這類狀況下瀏覽器都邑阻攔這個新窗口,除非用戶設定對這個域名許可任何彈窗。

《瀏覽器父子窗口間通訊》

stackoverflow上能夠看到這個詮釋

The general rule is that popup blockers will engage if window.open or similar is invoked from javascript that is not invoked by direct user action. That is, you can call window.open in response to a button click without getting hit by the popup blocker, but if you put the same code in a timer event it will be blocked. Depth of call chain is also a factor – some older browsers only look at the immediate caller, newer browsers can backtrack a little to see if the caller’s caller was a mouse click etc. Keep it as shallow as you can to avoid the popup blockers.

起先當我點擊按鈕的時刻我先去經由過程收集要求接口獵取受權頁的銜接。在異步回調里獵取到了受權頁鏈接。此時再去用window.open去翻開這個鏈接。這個不是 direct user action。縱然能夠置信也是一個比較差的用戶體驗,由於造成了耽誤。所以修改后的計劃就是用戶點擊了關聯按鈕立時翻開一個blank窗口。同時異步去獵取受權頁鏈接。獵取后reload翻開的受權窗口的地點為獵取到的銜接。這就不會致使 popup blocked 的徵象發生了。

形貌到這裡能夠假如沒有受權頁開闢履歷的人能夠照樣沒法明白是怎樣封閉子頁面的。當用戶翻開受權頁后,用戶點擊受權按鈕

《瀏覽器父子窗口間通訊》

此時頁面會跳轉到一個用戶指定的uri。假如未指定的話,會直接顯現authorize code在窗口中,這一般不是我們想要的。我們需要用這個受權碼去調換token,token是真正能夠登錄用戶賬戶的暫時憑據。所以一般是用戶指定一個uri,這個uri能夠是一個後端接口,受權窗口被用戶受權後會以querystring的情勢帶上code的參數跳轉到我們供應的uri。這個時刻後端接口能夠獵取到受權碼去實行換token的操縱。以後接口返回一個text/html的response,response 返回的內容大抵以下:

<html>
    <head>
    <meta charset="UTF-8"/>
        <title>test</title>
    </head>
    <body>
    賬號關聯勝利
    </body>
    <script>
    window.opener.postMessage('close_child_window', '*');
    </script>
</html>
    原文作者:AnthonyYY
    原文地址: https://segmentfault.com/a/1190000015127237
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞