日韩欧美人妻无码精品白浆,www.大香蕉久久网,狠狠的日狠狠的操,日本好好热在线观看

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網站管理員

前端監(jiān)控用戶停留時長

freeflydom
2025年6月4日 11:58 本文熱度 553

在前端監(jiān)控用戶在當前界面的停留時長(也稱為“頁面停留時間”或“Dwell Time”)是用戶行為分析中非常重要的指標。它可以幫助我們了解用戶對某個頁面的興趣程度、內容質量以及用戶體驗。

停留時長監(jiān)控的挑戰(zhàn)

監(jiān)控停留時長并非簡單地計算進入和離開的時間差,因為它需要考慮多種復雜情況:

  1. 用戶切換標簽頁或最小化瀏覽器: 頁面可能仍在后臺運行,但用戶并未真正“停留”在該界面。
  2. 瀏覽器關閉或崩潰: 頁面沒有正常卸載,可能無法觸發(fā) unload 事件。
  3. 網絡問題: 數(shù)據上報可能失敗。
  4. 單頁應用 (SPA) : 在 SPA 中,頁面切換不會觸發(fā)傳統(tǒng)的頁面加載和卸載事件,需要監(jiān)聽路由變化。
  5. 長時間停留: 如果用戶停留時間很長,一次性上報可能導致數(shù)據丟失(例如,瀏覽器或電腦崩潰)。

實現(xiàn)監(jiān)測的思路和方法

我們將結合多種 Web API 來實現(xiàn)一個健壯的停留時長監(jiān)控方案。

1. 基礎方案:頁面加載與卸載 (適用于傳統(tǒng)多頁應用)

這是最基本的方案,通過記錄頁面加載時間和卸載時間來計算停留時長。

// page-duration-tracker.js
let startTime = 0; // 頁面加載時間
let pageId = '';   // 頁面唯一標識符,例如 URL 路徑
/**
 * 上報頁面停留時長數(shù)據
 * @param {string} id 頁面唯一標識
 * @param {number} duration 停留時長 (毫秒)
 * @param {boolean} isUnload 是否是頁面卸載時上報
 */
function sendPageDuration(id, duration, isUnload = false) {
    const data = {
        pageId: id,
        duration: duration,
        timestamp: Date.now(),
        eventType: isUnload ? 'page_unload' : 'page_hide',
        // 可以添加更多上下文信息,如用戶ID、會話ID、瀏覽器信息等
        userAgent: navigator.userAgent,
        screenWidth: window.screen.width,
        screenHeight: window.screen.height
    };
    console.log('上報頁面停留時長:', data);
    // 使用 navigator.sendBeacon 確保在頁面卸載時也能發(fā)送數(shù)據
    // sendBeacon 適合發(fā)送少量數(shù)據,且不會阻塞頁面卸載
    if (navigator.sendBeacon) {
        navigator.sendBeacon('/api/page-duration', JSON.stringify(data));
    } else {
        // Fallback for older browsers (可能會阻塞頁面卸載或失敗)
        fetch('/api/page-duration', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(data),
            keepalive: true // 嘗試在頁面卸載時保持連接
        }).catch(e => console.error('發(fā)送停留時長失敗:', e));
    }
}
// 頁面加載時記錄開始時間
window.addEventListener('load', () => {
    startTime = Date.now();
    pageId = window.location.pathname; // 使用路徑作為頁面ID
    console.log(`頁面 ${pageId} 加載,開始計時: ${startTime}`);
});
// 頁面卸載時計算并上報時長
// 'beforeunload' 可能會被瀏覽器阻止,'pagehide' 更可靠,尤其是在移動端
window.addEventListener('pagehide', () => {
    if (startTime > 0) {
        const duration = Date.now() - startTime;
        sendPageDuration(pageId, duration, true);
        startTime = 0; // 重置,避免重復上報
    }
});
// 對于某些瀏覽器或場景,'beforeunload' 仍然有用,作為補充
window.addEventListener('beforeunload', () => {
    if (startTime > 0) {
        const duration = Date.now() - startTime;
        sendPageDuration(pageId, duration, true);
        startTime = 0;
    }
});
// 在你的 HTML 中引入此腳本
// <script src="page-duration-tracker.js"></script>

代碼講解:

  • startTime: 記錄頁面加載時的 Unix 時間戳。

  • pageId: 標識當前頁面,這里簡單地使用了 window.location.pathname。在實際應用中,你可能需要更復雜的 ID 策略(如路由名稱、頁面 ID 等)。

  • sendPageDuration(id, duration, isUnload): 負責將頁面 ID 和停留時長發(fā)送到后端。

    • navigator.sendBeacon(): 推薦用于在頁面卸載時發(fā)送數(shù)據。它不會阻塞頁面卸載,且即使頁面正在關閉,也能保證數(shù)據發(fā)送。
    • fetch({ keepalive: true })keepalive: true 選項允許 fetch 請求在頁面卸載后繼續(xù)發(fā)送,作為 sendBeacon 的備用方案。
  • window.addEventListener('load', ...): 在頁面完全加載后開始計時。

  • window.addEventListener('pagehide', ...): 當用戶離開頁面(切換標簽頁、關閉瀏覽器、導航到其他頁面)時觸發(fā)。這是一個更可靠的事件,尤其是在移動端,因為它在頁面進入“后臺”狀態(tài)時觸發(fā)。

  • window.addEventListener('beforeunload', ...): 在頁面即將卸載時觸發(fā)。它比 pagehide 觸發(fā)得更早,但可能會被瀏覽器阻止(例如,如果頁面有未保存的更改)。作為補充使用。

2. 考慮用戶活躍狀態(tài):Visibility API

當用戶切換標簽頁或最小化瀏覽器時,頁面可能仍在運行,但用戶并未真正“停留”。document.visibilityState 和 visibilitychange 事件可以幫助我們識別這種狀態(tài)。

// page-duration-tracker-with-visibility.js
let startTime = 0;
let totalActiveTime = 0; // 累計活躍時間
let lastActiveTime = 0;  // 上次活躍時間戳
let pageId = '';
function sendPageDuration(id, duration, eventType) {
    const data = {
        pageId: id,
        duration: duration,
        timestamp: Date.now(),
        eventType: eventType,
        // ... 其他上下文信息
    };
    console.log('上報頁面停留時長:', data);
    if (navigator.sendBeacon) {
        navigator.sendBeacon('/api/page-duration', JSON.stringify(data));
    } else {
        fetch('/api/page-duration', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), keepalive: true }).catch(e => console.error('發(fā)送停留時長失敗:', e));
    }
}
function startTracking() {
    startTime = Date.now();
    lastActiveTime = startTime;
    totalActiveTime = 0;
    pageId = window.location.pathname;
    console.log(`頁面 ${pageId} 加載,開始計時 (總時長): ${startTime}`);
}
function stopTrackingAndReport(eventType) {
    if (startTime > 0) {
        // 如果頁面當前是可見的,需要將從上次活躍到現(xiàn)在的這段時間也計入活躍時間
        if (document.visibilityState === 'visible') {
            totalActiveTime += (Date.now() - lastActiveTime);
        }
        sendPageDuration(pageId, totalActiveTime, eventType);
        startTime = 0; // 重置
        totalActiveTime = 0;
        lastActiveTime = 0;
    }
}
// 頁面加載時開始追蹤
window.addEventListener('load', startTracking);
// 監(jiān)聽頁面可見性變化
document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') {
        // 頁面變?yōu)椴豢梢?,暫停計時,并將當前活躍時間累加
        totalActiveTime += (Date.now() - lastActiveTime);
        console.log(`頁面 ${pageId} 變?yōu)椴豢梢?,累加活躍時間: ${totalActiveTime}`);
    } else {
        // 頁面變?yōu)榭梢?,恢復計時
        lastActiveTime = Date.now();
        console.log(`頁面 ${pageId} 變?yōu)榭梢姡謴陀嫊r: ${lastActiveTime}`);
    }
});
// 頁面卸載時上報最終時長
window.addEventListener('pagehide', () => stopTrackingAndReport('page_hide'));
window.addEventListener('beforeunload', () => stopTrackingAndReport('page_unload'));
// 考慮長時間停留:定時上報心跳或分段數(shù)據
// 例如,每隔 30 秒上報一次當前活躍時間,并重置計數(shù)器
let heartbeatInterval;
window.addEventListener('load', () => {
    startTracking(); // 確保在 load 事件中啟動計時
    heartbeatInterval = setInterval(() => {
        if (document.visibilityState === 'visible' && startTime > 0) {
            const currentActiveTime = Date.now() - lastActiveTime;
            totalActiveTime += currentActiveTime;
            lastActiveTime = Date.now(); // 重置上次活躍時間
            console.log(`心跳上報 ${pageId} 活躍時間: ${currentActiveTime}ms, 累計: ${totalActiveTime}ms`);
            // 可以選擇上報當前心跳的活躍時間,或者累計活躍時間
            sendPageDuration(pageId, currentActiveTime, 'heartbeat'); // 上報當前心跳時間
        }
    }, 30 * 1000); // 每 30 秒
});
// 頁面卸載時清除心跳定時器
window.addEventListener('pagehide', () => {
    clearInterval(heartbeatInterval);
    stopTrackingAndReport('page_hide');
});
window.addEventListener('beforeunload', () => {
    clearInterval(heartbeatInterval);
    stopTrackingAndReport('page_unload');
});

代碼講解:

  • totalActiveTime: 存儲用戶在頁面可見狀態(tài)下的累計停留時間。

  • lastActiveTime: 記錄頁面上次變?yōu)榭梢姷臅r間戳。

  • document.addEventListener('visibilitychange', ...): 監(jiān)聽頁面可見性變化。

    • 當頁面變?yōu)?nbsp;hidden 時,將從 lastActiveTime 到當前的時間差累加到 totalActiveTime
    • 當頁面變?yōu)?nbsp;visible 時,更新 lastActiveTime 為當前時間,表示重新開始計算活躍時間。
  • 心跳上報setInterval 每隔一段時間(例如 30 秒)檢查頁面是否可見,如果是,則計算并上報當前時間段的活躍時間。這有助于在用戶長時間停留但未觸發(fā) pagehide 或 beforeunload 的情況下(例如瀏覽器崩潰、電腦關機),也能獲取到部分停留數(shù)據。

3. 針對單頁應用 (SPA) 的解決方案

SPA 的頁面切換不會觸發(fā)傳統(tǒng)的 load 或 unload 事件。我們需要監(jiān)聽路由變化來模擬頁面的“加載”和“卸載”。

// page-duration-tracker-spa.js
let startTime = 0;
let totalActiveTime = 0;
let lastActiveTime = 0;
let currentPageId = '';
function sendPageDuration(id, duration, eventType) {
    const data = {
        pageId: id,
        duration: duration,
        timestamp: Date.now(),
        eventType: eventType,
        // ... 其他上下文信息
    };
    console.log('上報 SPA 頁面停留時長:', data);
    if (navigator.sendBeacon) {
        navigator.sendBeacon('/api/page-duration', JSON.stringify(data));
    } else {
        fetch('/api/page-duration', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), keepalive: true }).catch(e => console.error('發(fā)送停留時長失敗:', e));
    }
}
function startTrackingNewPage(newPageId) {
    // 結束舊頁面的追蹤并上報
    if (currentPageId && startTime > 0) {
        if (document.visibilityState === 'visible') {
            totalActiveTime += (Date.now() - lastActiveTime);
        }
        sendPageDuration(currentPageId, totalActiveTime, 'route_change');
    }
    // 開始新頁面的追蹤
    startTime = Date.now();
    lastActiveTime = startTime;
    totalActiveTime = 0;
    currentPageId = newPageId;
    console.log(`SPA 頁面 ${currentPageId} 加載,開始計時: ${startTime}`);
}
// 監(jiān)聽頁面可見性變化 (與多頁應用相同)
document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') {
        totalActiveTime += (Date.now() - lastActiveTime);
        console.log(`SPA 頁面 ${currentPageId} 變?yōu)椴豢梢?,累加活躍時間: ${totalActiveTime}`);
    } else {
        lastActiveTime = Date.now();
        console.log(`SPA 頁面 ${currentPageId} 變?yōu)榭梢姡謴陀嫊r: ${lastActiveTime}`);
    }
});
// 監(jiān)聽路由變化
// 對于 React Router, Vue Router 等,你需要監(jiān)聽它們提供的路由事件
// 這里以原生的 History API 監(jiān)聽為例
window.addEventListener('popstate', () => {
    startTrackingNewPage(window.location.pathname);
});
const originalPushState = history.pushState;
history.pushState = function() {
    originalPushState.apply(history, arguments);
    startTrackingNewPage(window.location.pathname);
};
const originalReplaceState = history.replaceState;
history.replaceState = function() {
    originalReplaceState.apply(history, arguments);
    // replaceState 通常不視為頁面切換,但如果需要,也可以在這里觸發(fā)
    // startTrackingNewPage(window.location.pathname);
};
// 首次加載時啟動追蹤
window.addEventListener('load', () => {
    startTrackingNewPage(window.location.pathname);
});
// 頁面最終卸載時上報(用戶關閉瀏覽器或離開整個 SPA)
window.addEventListener('pagehide', () => {
    if (currentPageId && startTime > 0) {
        if (document.visibilityState === 'visible') {
            totalActiveTime += (Date.now() - lastActiveTime);
        }
        sendPageDuration(currentPageId, totalActiveTime, 'app_unload');
        currentPageId = ''; // 重置
        startTime = 0;
        totalActiveTime = 0;
        lastActiveTime = 0;
    }
});
window.addEventListener('beforeunload', () => {
    if (currentPageId && startTime > 0) {
        if (document.visibilityState === 'visible') {
            totalActiveTime += (Date.now() - lastActiveTime);
        }
        sendPageDuration(currentPageId, totalActiveTime, 'app_unload');
        currentPageId = '';
        startTime = 0;
        totalActiveTime = 0;
        lastActiveTime = 0;
    }
});
// 心跳上報 (與多頁應用相同,確保在 load 事件中啟動)
let heartbeatInterval;
window.addEventListener('load', () => {
    heartbeatInterval = setInterval(() => {
        if (document.visibilityState === 'visible' && currentPageId) {
            const currentActiveTime = Date.now() - lastActiveTime;
            totalActiveTime += currentActiveTime;
            lastActiveTime = Date.now();
            console.log(`SPA 心跳上報 ${currentPageId} 活躍時間: ${currentActiveTime}ms, 累計: ${totalActiveTime}ms`);
            sendPageDuration(currentPageId, currentActiveTime, 'heartbeat');
        }
    }, 30 * 1000); // 每 30 秒
});
window.addEventListener('pagehide', () => clearInterval(heartbeatInterval));
window.addEventListener('beforeunload', () => clearInterval(heartbeatInterval));

代碼講解:

  • startTrackingNewPage(newPageId): 這是 SPA 方案的核心函數(shù)。

    • 每次路由變化時調用它。
    • 它會先計算并上報前一個頁面的停留時長。
    • 然后重置計時器,開始計算新頁面的停留時長。
  • 路由監(jiān)聽:

    • window.addEventListener('popstate', ...): 監(jiān)聽瀏覽器前進/后退按鈕導致的 URL 變化。

    • history.pushState 和 history.replaceState 的劫持: SPA 框架通常通過這些方法來改變 URL 而不觸發(fā)頁面刷新。通過劫持它們,我們可以在路由發(fā)生變化時觸發(fā) startTrackingNewPage。

    • 注意: 如果你使用 React Router, Vue Router 等,它們通常提供了更方便的路由守衛(wèi)或事件鉤子來監(jiān)聽路由變化,你應該優(yōu)先使用框架提供的 API。例如:

      • React Router: 在 useEffect 中監(jiān)聽 location.pathname 變化。
      • Vue Router: 使用 router.beforeEach 或 router.afterEach 導航守衛(wèi)。

總結與最佳實踐

  1. 區(qū)分多頁應用和單頁應用: 根據你的應用類型選擇合適的監(jiān)聽策略。
  2. 結合 Visibility API: 確保只計算用戶真正“活躍”在頁面上的時間。
  3. 使用 navigator.sendBeacon: 確保在頁面卸載時數(shù)據能夠可靠上報。
  4. 心跳上報: 對于長時間停留的頁面,定期上報數(shù)據,防止數(shù)據丟失。
  5. 唯一頁面標識: 確保每個頁面都有一個唯一的 ID,以便后端能夠正確聚合數(shù)據。
  6. 上下文信息: 上報數(shù)據時,包含用戶 ID、會話 ID、設備信息、瀏覽器信息等,以便更深入地分析用戶行為。
  7. 后端處理: 后端需要接收這些數(shù)據,并進行存儲、聚合和分析。例如,可以計算每個頁面的平均停留時間、總停留時間、不同用戶群體的停留時間等。
  8. 數(shù)據準確性: 即使有了這些方案,停留時長仍然是一個近似值,因為總有一些極端情況(如斷網、瀏覽器崩潰)可能導致數(shù)據丟失。目標是盡可能提高數(shù)據的準確性和覆蓋率。

轉自https://juejin.cn/post/7510803578505134119


該文章在 2025/6/4 11:59:09 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業(yè)務管理,結合碼頭的業(yè)務特點,圍繞調度、堆場作業(yè)而開發(fā)的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統(tǒng),標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

亚洲av色情一区二区| 一区二区 搜索| 台湾佬中文娱乐网黄色电影| 国产一级片香蕉| 色松鼠网站免久久久| 欧美αV综合一区二区三区| 日韩欧美精品直播| 制服 另类 中文 欧美| 狠狠做激情久久| 顶级欧美日韩成人| 亚洲天堂情网站| 69国产精品一区二区在线| 处文一级黄色按摩片播放| 日韩精品视频综合| 精品久久人妻一区二区| 精品一区2区| 日韩 不卡 一区| 97国产精品视频不卡| 免费视频久久二区三区| 一级小黄片视频| 欧美亚洲日韩性图专区一二区蜜臀| 精品蝎子一区二区三区| 日韩精品欧美色图二区| 午夜性色av| 日本久久久草一区二区| 另类 久久 一区| 日本浣肠一区二区视频| 欧美亚洲色情| 国产夫妻3P| 麟游县| 91超高清成人视频| 国产92调教| 深爱午夜在线网| 国产精品 mp4| 久久综合少妇中文字幕| 亚洲成人成人小说| 欧美午夜黄色电影在线观看| 99亚洲电影在线| 成人在线精品视频| 日本在线免费观看 www 后入| 亚洲国产一区二区三区四区|