文章目录
自动采集的原理(核心思路)原理分为两类一、传统页面(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)SDK 自动生成一个 page_view 事件打包当前页面信息(URL、title、referrer、UTM 参数等)上报到日志后台
DOMContentLoaded
示意:
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() 修改 URL,但不会刷新页面。
history.replaceState()
日志 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) | |
| 页面标题 | |
| Referrer(来源页) | |
| UTM 参数(营销来源) | 从 query 中解析 |
| device 信息 | UA 解析 |
| 屏幕尺寸 | 等 |
| 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。但 XHR/fetch 往往还没发出去页面就死了。
visibilitychange
例如以下代码可能 70% 情况会丢:
window.addEventListener("beforeunload", () => {
fetch("/log", { method: "POST", body: data });
});
请求完全来不及发送,浏览器强制终止。
难点 2:beforeunload / unload 行为不稳定
浏览器为提升用户体验,近年来不断弱化 beforeunload/unload:
Safari:很多情况下不会触发 Chrome:
unload 不允许执行异步移动端浏览器经常压根不触发这两个事件
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 没有真实离开事件。常用方案包括 、visibilitychange 触发、SPA 路由 hook、本地持久化与重试。
sendBeacon()
















暂无评论内容