摘要:背景有一個任務(wù)非常耗時會消耗后臺大量算力,所以在退出頁面的時候,要求前端這邊發(fā)送一個請求來殺死任務(wù)。小結(jié)本文總共講了三個,和,這個估計知道的人比較少,以后遇到前端埋點和頁面卸載前發(fā)送請求的需求,記得使用這三個。
背景:
有一個任務(wù)非常耗時會消耗后臺大量算力,所以在退出頁面的時候,要求前端這邊發(fā)送一個請求來殺死任務(wù)。
一開始以為這個需求非常簡單,就是在進入其他路由前,發(fā)送一下請求,殺死一下任務(wù)就好了。
然而現(xiàn)實狠狠的打了我的臉,因為退出頁面的場景不止切換路由~
退出頁面場景:還在本網(wǎng)站,跳到其他路由
刷新頁面/關(guān)閉頁面也需要發(fā)送請求來殺死任務(wù)
還在本網(wǎng)站,跳到其他路由這個比較簡單,在Vue中可以通過路由離開的鉤子beforeRouteLeave來實現(xiàn):
beforeRouteLeave(to, from, next) { if (任務(wù)運行中) { // 發(fā)送請求 }else{ next(true) // 用戶離開 } }
刷新頁面/關(guān)閉頁面的情況:
然而在刷新頁面的時候,beforeRouteLeave并不會執(zhí)行,接著想到了下面這兩個API.
beforeunload和unload beforeunload 當瀏覽器窗口關(guān)閉或者刷新時觸發(fā):介紹:
使用這個API可以阻止頁面直接關(guān)閉,用戶通過點擊確定/取消按鈕,來決定是否不關(guān)閉/刷新當前頁面。
在 chrome 下長這個樣子,你們肯定都見過:
如何使用
這個 API 的使用非常簡單,只要在頁面加載的時候監(jiān)聽一下此事件,在需要出現(xiàn)彈窗的時候return 一個可以轉(zhuǎn)化為 true 的值,就可以了。
// 頁面卸載之前 let killTask = false; // 是否殺死任務(wù) window.onbeforeunload = e => { if (任務(wù)運行 && 對應(yīng)頁面) { killTask = true; return "您可能有數(shù)據(jù)沒有保存"; // 在部分瀏覽器可以修改彈窗標題 } else { killTask = false; } // 沒有return一個可以轉(zhuǎn)化為true的值 就不會出現(xiàn)彈窗 };
出現(xiàn)此彈窗的瀏覽器行為:
以下行為是基于 chorme:
焦點:你沒有點擊取消/確定之前,焦點會一直在此彈窗上
你無法在出現(xiàn)彈窗的頁面上執(zhí)行任何操作
在其他頁面也只能執(zhí)行簡單的點擊操作,彈窗還是存在頁面中間,無法使用鍵盤,
鍵盤:鍵盤被綁定在彈窗上,只能通過按鍵Esc、Enter來執(zhí)行取消/確定操作
彈窗不是頁面的 dom,是瀏覽器的行為
用戶取消/確定,沒有回調(diào) API,無法得知
彈窗標題:
chrome 中刷新頁面的標題:重新加載此網(wǎng)站?
chrome 中關(guān)閉頁面的標題:離開此網(wǎng)站?
現(xiàn)在大部分瀏覽器都不允許修改彈窗的標題,這個是為了安全考慮,來保證用戶不受到錯誤信息的誤導,
迷茫:
一開始我以為既然可以攔截到用戶的刷新/關(guān)閉頁面的操作,出現(xiàn)了上面那個彈窗,這個需求就已經(jīng)做完了的時候。
然后發(fā)現(xiàn),瀏覽器竟然沒有提供用戶點擊確定/取消刷新頁面的回調(diào)。
到這里我陷入了迷茫,盯著beforeunload這個 API 思考了起了人生的意義(其實是在發(fā)呆),盯著盯著,從beforeunload的before我也就想到了unload這個 API。
瞬間又燃起了斗志,何不試試這個unload?
unload當頁面正在被卸載的時候觸發(fā)該事件介紹
當頁面正在被卸載的時候觸發(fā)該事件,該事件不可取消,為不可逆操作。
使用
直接監(jiān)聽該事件就可以了。
window.onunload = e => {}
結(jié)合需求:
killTask為beforeunload時定義的變量,每次進入回調(diào),都會給killTask賦值,使用這個值就可以判斷什么時候可以發(fā)送請求殺死任務(wù)。
window.onunload = e => { if (killTask && 對應(yīng)頁面) { // 發(fā)送請求 } };
到這里大家肯定以為已經(jīng)做出來了該需求,事實上,并沒有!
無法發(fā)送異步請求
我使用的是axios來發(fā)送請求,請求發(fā)出去了,但是被取消了,服務(wù)器那邊根本沒有收到請求,如下。
經(jīng)過一頓分析:發(fā)現(xiàn)是axios請求是異步的問題,谷歌之后發(fā)現(xiàn)axios不支持同步的請求
最后使用原生的XMLHttpRequest對象,讓請求同步
大功告成! 實際上,上面才是我第一次要發(fā)的內(nèi)容,而下面更好的解決方法!
當我把這篇文章發(fā)布在公眾號上,被奇舞周刊轉(zhuǎn)載了,上面一些大佬給我提了一些建議。
研究了一下,結(jié)果...好吧!我承認我是菜雞。
hey~ 不過這正是我寫博客的收獲之一,分享經(jīng)驗,收獲知識!
性能缺陷:XHR同步請求會阻礙頁面卸載,如果是刷新/跳轉(zhuǎn)頁面的話,頁面重新展示速度會變慢,導致性能問題。
畢竟向網(wǎng)絡(luò)發(fā)送請求并獲得響應(yīng)可能會超級慢,有可能是用戶網(wǎng)絡(luò)環(huán)境比較差,又或者是服務(wù)器掛了,請求一直沒返回回來...
基于性能問題,大佬們推薦使用Beacon代替XHR,然后經(jīng)過一番搜索...
Beacon APIBeacon API用于將少量數(shù)據(jù)通過post請求發(fā)送到服務(wù)器。
Beacon是非阻塞請求,不需要響應(yīng)
完美解決性能缺陷問題:瀏覽器將 Beacon 請求排隊讓它在空閑的時候執(zhí)行并立即返回控制
它在unload狀態(tài)下也可以異步發(fā)送,不阻塞頁面刷新/跳轉(zhuǎn)等操作。
所以Beacon可以完美解決上面提到的因XHR同步請求阻塞而引起的性能缺陷問題。
使用:navigator.sendBeacon()完整API:
let result = navigator.sendBeacon(url, data);
Beacon是掛在navigator下面的,上面就是它的完整API。
result是一個布爾值,代表這次發(fā)送請求的結(jié)果:
如果瀏覽器接受并且把請求排隊了則返回 tru
如果在這個過程中出現(xiàn)了問題就返回 false
navigator.sendBeacon接受兩個參數(shù):
url: 請求的 URL。請求是 POST 請求。
data: 要發(fā)送的數(shù)據(jù)。 數(shù)據(jù)類型可以是:ArrayBufferView, Blob, FormData,Sting。
來看一個用FormData來傳遞數(shù)據(jù)的栗子,你就懂了:
// 創(chuàng)建一個新的 FormData 并添加一個鍵值對 let data = new FormData(); data.append("hello", "world"); let result = navigator.sendBeacon("./src", data); if (result) { console.log("請求成功排隊 等待執(zhí)行"); } else { console.log("失敗"); }瀏覽器支持:
瀏覽器支持:Edge:14+,F(xiàn)irefox:31+,Chrome:39+,Opera:26+,IE:不支持。
雖然現(xiàn)在瀏覽器對sendBeacon的支持很好,我們對其做一下兼容性處理也是有必要的:
if (navigator.sendBeacon) { // Beacon 代碼 } else { // 回退到 XHR同步請求或者不做處理 }web wroker中使用Beacon
因為Beacon是掛在navigator 下面,而web worker也有navigator ,去找了一下,真的給我找到了。
這兒有一個MDN提供的栗子),可以點進去看一下。
PS:對web worker不熟悉的同學可以看我這篇文章
Beacon其他相關(guān)客戶端優(yōu)化:可以將 Beacon 請求合并到其他請求上,一同處理, 尤其在移動環(huán)境下。
Beacon更多的情況是用于做前端埋點,監(jiān)控用戶活動,它的初衷也基于此。
小結(jié)本文總共講了三個API,beforeunload、unload和Beacon,Beacon這個API估計知道的人比較少,以后遇到前端埋點和頁面卸載前發(fā)送請求的需求,記得使用這三個API。
以上2019.02.19
博客、前端積累文檔、公眾號、GitHub
參考資料:
MDN
頁面跳轉(zhuǎn)時,統(tǒng)計數(shù)據(jù)丟失問題探討
使用 Web Beacon API 記錄活動
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102061.html
摘要:本文的介紹的是如何設(shè)計一個通用的可以以較小的侵入性,自動上報前端的性能數(shù)據(jù)。具體的做法可以看我的上一篇文章在單頁應(yīng)用中,如何優(yōu)雅的監(jiān)聽的變化。三如何上報性能數(shù)據(jù)那么如何上報性能數(shù)據(jù)呢,我們第一反應(yīng)就是通過請求的形式來上報前端性能數(shù)據(jù)。 ??最近在做一個較為通用的前端性能監(jiān)控平臺,區(qū)別于前端異常監(jiān)控,前端的性能監(jiān)控主要需要上報和展示的是前端的性能數(shù)據(jù),包括首頁渲染時間、每個頁面的白屏時...
摘要:本文的介紹的是如何設(shè)計一個通用的可以以較小的侵入性,自動上報前端的性能數(shù)據(jù)。具體的做法可以看我的上一篇文章在單頁應(yīng)用中,如何優(yōu)雅的監(jiān)聽的變化。三如何上報性能數(shù)據(jù)那么如何上報性能數(shù)據(jù)呢,我們第一反應(yīng)就是通過請求的形式來上報前端性能數(shù)據(jù)。 ??最近在做一個較為通用的前端性能監(jiān)控平臺,區(qū)別于前端異常監(jiān)控,前端的性能監(jiān)控主要需要上報和展示的是前端的性能數(shù)據(jù),包括首頁渲染時間、每個頁面的白屏時...
摘要:本文的介紹的是如何設(shè)計一個通用的可以以較小的侵入性,自動上報前端的性能數(shù)據(jù)。具體的做法可以看我的上一篇文章在單頁應(yīng)用中,如何優(yōu)雅的監(jiān)聽的變化。三如何上報性能數(shù)據(jù)那么如何上報性能數(shù)據(jù)呢,我們第一反應(yīng)就是通過請求的形式來上報前端性能數(shù)據(jù)。 ??最近在做一個較為通用的前端性能監(jiān)控平臺,區(qū)別于前端異常監(jiān)控,前端的性能監(jiān)控主要需要上報和展示的是前端的性能數(shù)據(jù),包括首頁渲染時間、每個頁面的白屏時...
閱讀 1833·2021-10-09 09:44
閱讀 2705·2021-09-22 15:38
閱讀 2502·2021-09-09 09:33
閱讀 709·2021-09-07 09:58
閱讀 1833·2021-09-02 15:41
閱讀 2527·2019-08-30 15:55
閱讀 1806·2019-08-30 15:55
閱讀 551·2019-08-30 15:44