最近項目有個需求拷姿,查詢頁面每次點擊查詢之后,需要彈出一個新窗口顯示結(jié)果列表恋拷。并且在結(jié)果列表里面做定時刷新操作,如果結(jié)果里面的刷新功能沒有被用戶停用厅缺,而彈出的窗口被關(guān)掉了蔬顾,查詢頁面等到下一次刷新就要重新彈出一個窗口了。
我的設(shè)想是每打開一個新窗口時湘捎,查詢頁面就生成一個時間戳诀豁,作為新窗口的一個標(biāo)識。那么一次查詢就對應(yīng)唯一的一個彈窗窥妇。
由于需要定時刷新舷胜,在查詢頁面里,我可以建立一個數(shù)組記錄當(dāng)前已打開的新窗口活翩,用時間戳作為key烹骨。使用setInterval 做定時器,那么刷新時查詢頁面需要做的是:
1.遍歷新窗口的數(shù)組材泄,如果新窗口沒有被關(guān)閉沮焕,那么什么也不用做。
2如果新窗口已經(jīng)被關(guān)閉拉宗,看看在關(guān)閉前它是否還在刷新狀態(tài)峦树, 如果是辣辫,則用記錄的查詢參數(shù)重新打開窗口。如果關(guān)閉前已經(jīng)停止刷新魁巩,那么可以將窗口從數(shù)組中去除急灭。
通過上面的思路,可以設(shè)計這個記錄新窗口的數(shù)組谷遂,它包含的狀態(tài)數(shù)據(jù)為:查詢條件葬馋,時間戳,是否還在打開埋凯,是否還在刷新狀態(tài)点楼。
主要的判斷邏輯如下:
setInterval(() => {
const length = this.getTimerArray.length;
for (let i = 0; i < length; i++) {
const timerItem = this.getTimerArray[i];
// 頁面已關(guān)閉并且定時器已停止,則從列表中刪除
if (!timerItem.refresh && !timerItem.running) {
timerItem.toDelete = true;
} else if (!timerItem.running) {
// 需要刷新但頁面已關(guān)閉
const stamp = this.goPage(timerItem._params);
timerItem.stamp = stamp;
}
// 將列表中的所有定時器置為非運行狀態(tài)白对,由子頁面發(fā)送的消息置為運行狀態(tài)
timerItem.running = false;
}
// 處理列表,將已停止的定時器刪除
const timerArray = this.timerArray.filter((r) => !r.toDelete);
this.setTimerArray(timerArray);
}, this.getInterval);
然后需要考慮另一個問題:怎么知道打開的新頁面已關(guān)閉换怖?
也許可以new Window 生成一個對象甩恼,然后再打開,但這樣就需要把這個對象記錄到數(shù)組里面沉颂,但我想要一個更簡單地方法条摸,而不是再花費時間去管理它。
我的設(shè)想是通過子頁面向opener 持續(xù)發(fā)送消息铸屉,告訴opener 頁面還在钉蒲,一旦頁面被關(guān)閉,在它下次刷新時就會知道彻坛,因為子頁面已經(jīng)不發(fā)送消息了顷啼。
這里需要用到opener 對象。需要注意的是昌屉,當(dāng)調(diào)用window.open 方法時钙蒙,所傳的參數(shù)noopener和noreferrer不能為yes。否則间驮,子頁面的window.opener 對象將會是空值躬厌。
window.open(url, target);
子頁面向opener發(fā)送消息:
const { interval } = opts[0] || {};
this.interval = interval || REFRESH_INTERVAL;
this.childStamp = getPageStamp();
setInterval(() => {
window.opener.postMessage({
stamp: this.childStamp,
});
}, 1000);
子頁面會先從url拿到一個時間戳,然后每秒向opener發(fā)送一次消息竞帽,帶上這個時間戳扛施。opener通過時間戳去區(qū)分是從哪個子頁面發(fā)送過來的,將對應(yīng)的running 字段置為true.
opener 要接受屹篓,需要注冊一個事件
window.addEventListener('message', (msg) => {
// console.log('msg', msg);
const {
data: { refresh, stamp },
} = msg;
const length = this.getTimerArray.length;
for (let i = 0; i < length; i++) {
const timerItem = this.getTimerArray[i];
if (timerItem.stamp === stamp) {
if (typeof refresh !== 'undefined') {
timerItem.refresh = refresh;
}
timerItem.running = true;
}
}
});
message事件接收的是postMessage發(fā)送的消息疙渣。事實上子頁面的opener就是主頁面,是opener調(diào)了postMessage方法抱虐,然后在自己的事件里接收了昌阿。