HOW – 日志数据上报之 PageView 采集和 PageLeave 采集

文章目录

自动采集的原理(核心思路)原理分为两类一、传统页面(Multi-Page App/Multi-Page Website)二、单页面应用(SPA:React / Vue / Angular)三、补充机制(保证准确度)

自动采集时具体采集哪些数据总结扩展:page_leave 的采集难点难点难点 1:页面关闭时请求无法保证送达难点 2:beforeunload / unload 行为不稳定难点 3:visibilitychange 不一定代表“离开页面”难点 4:SPA(单页应用)没有真正“离开页面”难点 5:网络环境影响更明显
业界常用的解决方案(避免丢事件)方案 1:使用 Beacon API(最标准)方案 2:使用 visibilitychange 作为离开信号方案 3:SPA 使用路由事件模拟 page_leave方案 4:本地缓存(LocalStorage/IndexedDB)+ 重试方案 5:延迟触发 / 去重策略
总结

不少前端埋点 / 日志采集工具(如神策、GrowingIO、Mixpanel、GA4、内建埋点 SDK 等)提供 自动采集页面浏览事件(
page_view
的能力。

这些工具的 核心原理本质上都是:监听页面的生命周期变化并自动上报

下面按照前端技术机制说明清楚。


自动采集的原理(核心思路)

虽然不同 SDK 实现细节不同,但通常都由 浏览器原生事件 + History API 劫持 实现。

原理分为两类

一、传统页面(Multi-Page App/Multi-Page Website)

只要浏览器真正 加载一个新页面,就会自动触发:


window.onload

DOMContentLoaded

visibilitychange
(从后台切到前台)
beforeunload
/
unload

采集逻辑:

页面加载完成后(例如
onload

DOMContentLoaded
)SDK 自动生成一个 page_view 事件打包当前页面信息(URL、title、referrer、UTM 参数等)上报到日志后台

示意:


window.addEventListener("load", () => {
  sendEvent("page_view", {
    url: location.href,
    title: document.title,
    referrer: document.referrer
  });
});

这对传统页面网站很简单,换页面 = 换文档 = 触发 load。


二、单页面应用(SPA:React / Vue / Angular)

SPA 不会刷新页面,URL 变化不会触发生命周期事件,因此 SDK 需要:

1. 劫持 History API

浏览器 SPA 常用
history.pushState()

history.replaceState()
修改 URL,但不会刷新页面。

日志 SDK 通常会:

重写(patch)
pushState

replaceState
监听
popstate
(浏览器前进/后退)

实现:


const rawPushState = history.pushState;
history.pushState = function (...args) {
  rawPushState.apply(history, args);
  emitPageChange();
};

window.addEventListener("popstate", emitPageChange);

只要 URL 变化,就自动触发一个 page_view。

2. 监听前端路由库

有些 SDK 还会对:

Vue RouterReact RouterAngular Router

进行 hook,在路由跳转完成后发 page_view。


三、补充机制(保证准确度)

visibilitychange(页面切入前台)

当用户从后台切回页面时,有些 SDK 会视为新的曝光:


document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "visible") sendEvent("page_view");
});

hashchange(锚点页面)

如果页面 URL 使用
#
作为路由(如 Vue 的 hash 模式),SDK 会监听:


window.addEventListener("hashchange", emitPageChange);

自动采集时具体采集哪些数据

一般包括:

字段 来源
URL(含 query)
location.href
页面标题
document.title
Referrer(来源页)
document.referrer
UTM 参数(营销来源) 从 query 中解析
device 信息 UA 解析
屏幕尺寸
window.innerWidth
session ID / user ID SDK 维护

总结

日志采集工具的 自动 page_view = 浏览器事件监听 + History API 劫持 + 路由 hook

用很简洁的图表示:


Page Loaded → onload/DOMReady → page_view
SPA URL Change → history.pushState/popstate → page_view
Hash Change → hashchange → page_view
Tab Active → visibilitychange → page_view

扩展:page_leave 的采集难点


page_view
(页面进入)比较容易自动采集,但
page_leave
(页面离开)是前端埋点中最困难、最容易丢失的事件之一

难点

因为「离开页面」本质上是用户即将关闭 / 跳出 / 切换 / 刷新的瞬间,而这个瞬间:

网络请求不稳定页面生命周期很短JS 执行被中断部分 API 不可靠

难点 1:页面关闭时请求无法保证送达

当用户:

关闭 tab浏览器退出刷新页面前往其他网站

页面会在很短时间内执行
beforeunload

visibilitychange
。但 XHR/fetch 往往还没发出去页面就死了

例如以下代码可能 70% 情况会丢:


window.addEventListener("beforeunload", () => {
  fetch("/log", { method: "POST", body: data });
});

请求完全来不及发送,浏览器强制终止。


难点 2:beforeunload / unload 行为不稳定

浏览器为提升用户体验,近年来不断弱化 beforeunload/unload

Safari:很多情况下不会触发
unload
Chrome:
beforeunload
不允许执行异步移动端浏览器经常压根不触发这两个事件

所以日志 SDK 不能依赖 unload 系列事件


难点 3:visibilitychange 不一定代表“离开页面”


visibilitychange
在以下情况都会触发:

切换到另一个 tab打开系统菜单、切浏览器跳出到 App(移动端)用户仅仅锁屏

但此时用户不一定是真离开页面。误报可能很多。


难点 4:SPA(单页应用)没有真正“离开页面”

在 React / Vue / Angular 中:

路由切换只是改变内容,没有 unload所以 page_leave 无法通过 unload 方式判断必须依赖前端路由事件

但不同框架(Vue Router / React Router)行为不同,SDK 很难“一刀切”。


难点 5:网络环境影响更明显

用户关闭页面时网络瞬断、切前后台等情况更多见,请求丢失率比普通事件高。


业界常用的解决方案(避免丢事件)

方案 1:使用 Beacon API(最标准)

浏览器提供
navigator.sendBeacon()
专门用于在页面即将卸载时发送数据。

特点:

异步、非阻塞浏览器保证尽力发送(不会被中断)可靠性高

示例:


window.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    navigator.sendBeacon("/log", JSON.stringify({ event: "page_leave" }));
  }
});

这几乎是现代 SDK 采集 page_leave 的标准做法。


方案 2:使用 visibilitychange 作为离开信号

采用:


document.visibilityState === "hidden"

作为“离开页面”依据,比 unload 更可靠。但由于会误触,常与节流/去重结合。


方案 3:SPA 使用路由事件模拟 page_leave

对于单页应用:


路由跳转时:
1. 触发当前页面的 page_leave
2. 接着触发新页面的 page_view

例如 Vue Router:


router.beforeEach((to, from, next) => {
  report('page_leave', from.fullPath);
  next();
});

这解决 SPA 中无法依赖 unload 的问题。


方案 4:本地缓存(LocalStorage/IndexedDB)+ 重试

当 page_leave 数据来不及发:

先写入 LocalStorage下次打开页面时继续重传

保证高可用性。

神策、Mixpanel、GA4 都采用类似机制。


方案 5:延迟触发 / 去重策略

由于 visibilitychange 可能频繁触发:

切换后台 = hidden切回前台 = visible

SDK 通常:

只在 hidden 时发送连续 hidden 需要合并限制在会话结束时发送一次

总结

page_leave 难点主要在于页面卸载阶段 JS 容易被中断、请求无法保证送达、浏览器对 unload 事件支持不一致、visibilitychange 可能误报、SPA 没有真实离开事件。常用方案包括
sendBeacon()
、visibilitychange 触发、SPA 路由 hook、本地持久化与重试。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容