頁面跳轉時,統計數據丟失問題探討

爲了更好地瞭解用戶對產品的使用狀況,業務中,咱們常常會收到埋點統計的需求,好比:我要說話前端

  • 收集一段時間內用戶光標在頁面中的運動狀況,包括光標移動、點擊等行爲
  • 統計用戶滾屏行爲
  • 統計用戶在站點的停留時長
  • 收集頁面連接的點擊數量等

不管是移動端仍是 PC 端,相信不少朋友都遇到了這麼幾個十分讓人頭疼的問題:我要說話git

  • 統計某個連接的點擊量,可是這個連接點擊後直接跳轉走了
  • 統計頁面時長問題,unload 的時候發送的統計丟失了
  • 統計腳本尚未初始化,用戶不感興趣已經走人了等

若是咱們把這樣的數據交給了產品同窗,可能會讓他們對用戶行爲產生錯誤的認知,必定程度上影響產品的下一步改善。我要說話github

上面提到的問題,從技術角度能夠概括爲兩點:我要說話編程

  • 用戶關閉頁面過早,統計腳本還未加載/初始化完成
  • 用戶關閉或者跳出頁面的時候,請求未發出

針對第一點,機率較小,通常的處理方式就是,不要把統計腳本參合到其餘腳本中,單獨加載,而且放在前頭,讓它優先加載。不少公司的作法是,不讓開發者關心統計腳本的加載,用戶請求頁面的時候,Nginx 會在 Body 開始標籤位置注入一段腳本。我要說話跨域

對於問題二,處理方案就有不少了。我要說話數組

1. 阻塞式的 Ajax 請求我要說話瀏覽器

還記得 XMLHttpRequest::open 方法的第三個參數吧,若是設置爲 false 就是同步加載,我要說話app

window.addEventListener('unload', function(event) {
var xhr = new XMLHttpRequest(),
xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
xhr.open('post', '/log', false); // 同步請求
xhr.send(data);
});

阻塞頁面關閉,固然能夠在 readState 爲 2 的時候就 abort 請求,由於咱們不關心響應的內容,只要請求發出去就好了。我要說話異步

2. 暴力的死循環我要說話函數

原理跟上面相似,只不過是使用一個空的死循環阻塞頁面關閉,我要說話

window.addEventListener('unload', function(event) {
send(data);
var now = +new Date;
while(new Date - now >= 10) {} // 阻塞 10ms
});

3. 發一個圖片請求阻塞我要說話

大部分瀏覽器都會等待圖片的加載,趁這個機會把統計數據發送出去我要說話

window.addEventListener('unload', function(event) {
send(data);
(new Image).src = 'http://example.com/s.gif';
});

以上提到的幾個方案都是一個原理,讓瀏覽器繼續保持阻塞狀態,等數據發送出去後再跳轉,這裏存在的問題是:我要說話

  • 少許瀏覽器下可能不奏效
  • 等待一下子再跳轉,用戶體驗上打了折扣,尤爲是移動端上

是否有更好的方案解決這個問題呢,前端同窗秉着「小強精神」也提出了兩個可實踐的方案。我要說話

不就是埋點統計數據嘛,非得在當前頁面發送出去?優化方案的思路具備必定的跳躍性,咱們考慮將數據在下跳頁中發送,那麼問題就轉換爲,如何將數據傳遞給下跳頁?我要說話

對於連接點擊量的統計,咱們能夠將連接信息經過 url 傳遞給下跳頁,傳遞思路以下:我要說話

1. url 傳參我要說話

經過數組標識一個連接的位置信息,如 [站點id,頁面id,模塊id,連接index],經過四個參數能夠唯一標識連接位置屬性,使用 URL param 參數將數組數據傳遞給下跳頁,等待由下跳頁將數據發送出去。我要說話

這裏存在的問題是,下跳頁中必須部署一樣的統計腳本,但對一個系統來講,這是很容易作到的。咱們也不會在本身的網頁上放其餘網站的連接吧,因此整個數據的統計都在一個閉環內。我要說話

2. 經過 window.name 傳遞數據我要說話

window.name 是瀏覽器給咱們開放的一個接口,設置該屬性的值後,即使頁面發生了跳轉,這個值依然不會變化,而且能夠跨域使用。我要說話

這裏存在的問題是,該屬性可能被開發者用於其餘途徑。咱們能夠限制開發者直接使用window.name,封裝接口,經過接口調用,如 aralejs 提供的 nameStorage我要說話

nameStorage.setItem(key, value);
nameStorage.getItem(key);
nameStorage.removeItem(key);

儲存形式爲:我要說話

    scheme                  nameStorage datas
| |
------------ ------------------------
nameStorage:origin-name?key1=value1&key2=value2
-----------
|
window origin name

以上雖然基本解決了數據丟失和體驗差的問題,可是這也很大程度依賴於開發者的編程習慣,如不能隨便玩耍 window.name;也對系統有必定的要求,必須在全部頁面上部署一樣的埋點腳本。我要說話

上面提到的各類方案,不乏黑科技,然而存在的問題仍是一大堆,若是團隊的開發者執行力不夠,中途容易出現各類麻煩。因此真正可以解決這個問題的,必然仍是瀏覽器自己!我要說話

爲何不能給用戶提供這樣一個 API,即便頁面跳轉了,也可以將上個頁面的請求發出去呢?慶幸的是,W3C 工做組也想到了這個問題,提出了 Beacon API 的 草案我要說話

Beacon API 容許開發者發送少許錯誤分析和上報的信息,它的特色很明顯:我要說話

  • 在空閒的時候異步發送統計,不影響頁面諸如 JS、CSS Animation 等執行
  • 即便頁面在 unload 狀態下,也會異步發送統計,不影響頁面過渡/跳轉到下跳頁
  • 可以被客戶端優化發送,尤爲在 Mobile 環境下,能夠將 Beacon 請求合併到其餘請求上,一同處理

sendBeacon 函數掛在在 navigator 上,在 unload 以前,這個函數必定是被初始化了的。其使用方式爲:我要說話

window.addEventListener('unload', function(event) {
navigator.sendBeacon('/collector', data);
});

navigator.sendBeacon(url, data);,第一個參數爲數據上報的地址,第二個參數爲要發送的數據,支持的數據格式有:ArrayBufferView, Blob, DOMString, 和 FormData。我要說話

Beacon 的還有一個很是實用的移動端使用場景,當用戶從瀏覽器切換到其餘 app 界面或者 Home 屏的時候,部分瀏覽器默認會中止頁面腳本的執行,若是在這個時候使用了 unload 時間,可能會讓你失望,由於 unload 事件並不會觸發,此時,Beacon 就派上用途了,它是不會受影響的。我要說話

本文是對頁面打點丟失問題的簡單探討,枚舉了咱們一般會用到的一些解決方案,可能不是很完善,若是你有更好的建議,能夠提出來。我要說話

不少問題,咱們絞盡腦汁,可能不多會考慮,這個問題是否是應該有咱們來解決,或者說這個問題交給誰處理是最恰當的。本文的探討能夠看到,瀏覽器自己纔是最好的問題解決方,當網站流量變大以後,上面提到的丟失問題就更加明顯,這也迫使瀏覽器自己作了改善,天然也在情理之中。

相關文章
相關標籤/搜索