有一個任務很是耗時會消耗後臺大量算力,因此在退出頁面的時候,要求前端這邊發送一個請求來殺死任務。前端
一開始覺得這個需求很是簡單,就是在進入其餘路由前,發送一下請求,殺死一下任務就行了。ios
然而現實狠狠的打了個人臉,由於退出頁面的場景不止切換路由~git
這個比較簡單,在Vue
中能夠經過路由離開的鉤子beforeRouteLeave
來實現:github
beforeRouteLeave(to, from, next) {
if (任務運行中) {
// 發送請求
}else{
next(true) // 用戶離開
}
}
複製代碼
刷新頁面/關閉頁面的狀況:web
然而在刷新頁面的時候,beforeRouteLeave
並不會執行,接着想到了下面這兩個API
.chrome
beforeunload
和unload
介紹:axios
使用這個API
能夠阻止頁面直接關閉,用戶經過點擊肯定/取消按鈕,來決定是否不關閉/刷新當前頁面。api
在 chrome 下長這個樣子,大家確定都見過:瀏覽器
如何使用安全
這個 API 的使用很是簡單,只要在頁面加載的時候監聽一下此事件,在須要出現彈窗的時候return 一個能夠轉化爲 true 的值,就能夠了。
// 頁面卸載以前
let killTask = false; // 是否殺死任務
window.onbeforeunload = e => {
if (任務運行 && 對應頁面) {
killTask = true;
return '您可能有數據沒有保存'; // 在部分瀏覽器能夠修改彈窗標題
} else {
killTask = false;
}
// 沒有return一個能夠轉化爲true的值 就不會出現彈窗
};
複製代碼
出現此彈窗的瀏覽器行爲:
如下行爲是基於 chorme:
焦點:你沒有點擊取消/肯定以前,焦點會一直在此彈窗上
你沒法在出現彈窗的頁面上執行任何操做
在其餘頁面也只能執行簡單的點擊操做,彈窗仍是存在頁面中間,沒法使用鍵盤,
鍵盤:鍵盤被綁定在彈窗上,只能經過按鍵Esc
、Enter
來執行取消/肯定操做
彈窗不是頁面的 dom,是瀏覽器的行爲
用戶取消/肯定,沒有回調 API,沒法得知
彈窗標題:
chrome 中刷新頁面的標題:從新加載此網站?
chrome 中關閉頁面的標題:離開此網站?
如今大部分瀏覽器都不容許修改彈窗的標題,這個是爲了安全考慮,來保證用戶不受到錯誤信息的誤導,
迷茫:
一開始我覺得既然能夠攔截到用戶的刷新/關閉頁面的操做,出現了上面那個彈窗,這個需求就已經作完了的時候。
而後發現,瀏覽器居然沒有提供用戶點擊肯定/取消刷新頁面的回調。
到這裏我陷入了迷茫,盯着beforeunload
這個 API 思考了起了人生的意義(實際上是在發呆),盯着盯着,從beforeunload
的before
我也就想到了unload
這個 API。
瞬間又燃起了鬥志,何不試試這個unload
?
unload
當頁面正在被卸載的時候觸發該事件介紹
當頁面正在被卸載的時候觸發該事件,該事件不可取消,爲不可逆操做。
使用
直接監聽該事件就能夠了。
window.onunload = e => {}
複製代碼
結合需求:
killTask
爲beforeunload
時定義的變量,每次進入回調,都會給killTask
賦值,使用這個值就能夠判斷何時能夠發送請求殺死任務。
window.onunload = e => {
if (killTask && 對應頁面) {
// 發送請求
}
};
複製代碼
到這裏你們確定覺得已經作出來了該需求,事實上,並無!
沒法發送異步請求
我使用的是axios
來發送請求,請求發出去了,可是被取消了,服務器那邊根本沒有收到請求,以下。
通過一頓分析:發現是axios
請求是異步的問題,谷歌以後發現axios不支持同步的請求
最後使用原生的XMLHttpRequest對象,讓請求同步
大功告成! 實際上,上面纔是我第一次要發的內容,而下面更好的解決方法!
當我把這篇文章發佈在公衆號上,被奇舞週刊轉載了,上面一些大佬給我提了一些建議。
研究了一下,結果...好吧!我認可我是菜雞。
hey~ 不過這正是我寫博客的收穫之一,分享經驗,收穫知識!
XHR同步請求會阻礙頁面卸載,若是是刷新/跳轉頁面的話,頁面從新展現速度會變慢,致使性能問題。
畢竟向網絡發送請求並得到響應可能會超級慢,有多是用戶網絡環境比較差,又或者是服務器掛了,請求一直沒返回回來...
基於性能問題,大佬們推薦使用Beacon代替XHR,而後通過一番搜索...
Beacon
是非阻塞請求,不須要響應Beacon
請求排隊讓它在空閒的時候執行並當即返回控制unload
狀態下也能夠異步發送,不阻塞頁面刷新/跳轉等操做。因此**Beacon
能夠完美解決上面提到的因XHR同步請求阻塞而引發的性能缺陷問題**。
navigator.sendBeacon()
完整API:
let result = navigator.sendBeacon(url, data);
複製代碼
Beacon
是掛在navigator
下面的,上面就是它的完整API。
result
是一個布爾值,表明此次發送請求的結果:
navigator.sendBeacon
接受兩個參數:
來看一個用FormData
來傳遞數據的栗子,你就懂了:
// 建立一個新的 FormData 並添加一個鍵值對
let data = new FormData();
data.append('hello', 'world');
let result = navigator.sendBeacon('./src', data);
if (result) {
console.log('請求成功排隊 等待執行');
} else {
console.log('失敗');
}
複製代碼
瀏覽器支持:Edge:14+,Firefox:31+,Chrome:39+,Opera:26+,IE:不支持。
雖然如今瀏覽器對sendBeacon
的支持很好,咱們對其作一下兼容性處理也是有必要的:
if (navigator.sendBeacon) {
// Beacon 代碼
} else {
// 回退到 XHR同步請求或者不作處理
}
複製代碼
由於Beacon
是掛在navigator
下面,而web worker也有navigator
,去找了一下,真的給我找到了。
這兒有一個MDN提供的栗子,能夠點進去看一下。
PS:對web worker不熟悉的同窗能夠看我這篇文章
本文總共講了三個API,beforeunload
、unload
和Beacon
,Beacon
這個API估計知道的人比較少,之後遇到前端埋點和頁面卸載前發送請求的需求,記得使用這三個API。
以上2019.02.19
博客、前端積累文檔、公衆號、GitHub、wx:OBkoro一、郵箱:obkoro1@foxmail.com
參考資料: