Web 埋点方案怎么定:从目标到落地

Web 埋点方案怎么定:从目标到落地

埋点指在页面或应用里按约定采集用户行为或业务事件(曝光、点击、停留、转化等),把数据发到分析平台或自建服务,用于产品迭代、运营效果、故障与体验优化。本文从目标 → 模型 → 采集方式 → 传输与性能 → 合规顺序,给一套可落地的选型思路,不绑定某一厂商 SDK;§9 用四个案例(博客阅读、电商曝光与漏斗、SPA 路由、批量与关页)说明字典与伪代码怎么写。

1. 先定目标:要回答什么问题

类型典型问题常见事件
流量哪些页面被访问、从哪来页面浏览 PV/UV、来源、落地页
行为用户点了什么、路径是否走通按钮点击、表单提交、步骤漏斗
内容哪篇文章/商品被看完列表曝光、详情停留、滚动深度
性能与异常慢在哪、错在哪LCP、JS 错误、接口失败(常与埋点并列,用 RUM/错误监控)

目标决定事件粒度优先级:不必一上来「全站每个像素都报」。


2. 事件模型:统一「谁在何时何地做了什么」

建议每条上报至少包含:

维度说明
事件名英文蛇形或点分,如 page_viewcta_clickorder_paid
时间客户端时间戳 + 服务端接收时间(用于对时与延迟分析)
用户/会话匿名设备 ID、登录用户 ID、会话 ID(同一会话多页面串联)
页面/环境URL、路由名、渠道、App/Web、版本号
业务属性article_idsku_idbutton_name(键名稳定、枚举可收敛)

文档化一份 《埋点字典》(事件名 + 属性 + 触发时机),前后端、产品、数据对齐,避免「同名不同义」。


3. 三种常见采集方式

方式做法优点注意
代码埋点在交互处显式调用 track('event', props)语义清晰、可控发版才能改,需评审字典
全埋点 / 无埋点SDK 监听大量 DOM 点击或路由上线快、覆盖面大噪声多、清洗成本高
可视化圈选运营在后台圈选元素绑定事件少改代码页面改版易失效,仍需规范

实际项目多为 代码埋点为主(核心转化路径)+ 少量全埋点/圈选(探索性分析)的混合。


4. 前端落地:放哪、何时发

  1. 页面级

    • 多页应用(MPA):每个 HTML 加载后上报 page_view
    • 单页应用(SPA):路由变化时上报(监听 history.pushState / 路由 afterEach),否则只会在首屏记一次。
  2. 交互级

    • 在按钮、卡片等稳定业务入口调用埋点,避免绑在易变的 class 上。
  3. 曝光级(列表项进入视口)

    • 使用 IntersectionObserver,注意节流去重(同一 item 只报一次或按策略重复)。
  4. 批量与卸载

    • 高频事件可先入队,定时批量发送;页面关闭时用 navigator.sendBeacon(若仍可用)降低丢失率。

5. 传输与性能

手段说明
HTTPS传输加密,防窃听与篡改。
异步、不阻塞主线程fetch/sendBeacon/Image 像素(兼容老环境);避免同步 XHR。
采样超大流量可对非关键事件做比例采样。
失败重试队列 + 退避,避免打爆采集端。

6. 合规与隐私(必做)

  • 告知与同意:Cookie/设备标识用于分析时,需符合当地法律与平台政策(如个人信息保护法、GDPR 等),隐私政策中说明用途。
  • 最小必要:不上报密码、完整身份证号、未脱敏手机号等;敏感字段脱敏或哈希
  • 可关闭:提供关闭统计或仅必要 Cookie 的选项(视产品形态而定)。

7. 平台选型(思路)

方向适用
SaaS(如常见统计与增长分析产品)省运维、报表多;注意数据出境与合同。
开源可自建(如部分 Web 分析项目)数据自持、轻量 PV;功能需自己补。
完全自建业务网关 + 日志管道 + OLAP;成本高、灵活度最高。

选型时同步考虑:是否支持 SPA是否提供服务端埋点(订单等以服务端为准)、是否与现有 ID 体系统一


8. 验收与治理

  • 联调:用抓包或平台「实时调试」核对事件名、属性是否与字典一致。
  • 监控:采集失败率、延迟突增要告警。
  • 版本:埋点 SDK 或协议变更做 版本号,避免新老客户端混用导致统计断层。

9. 案例说明(从业务到代码)

下面用四个互不冲突的场景,把前文落到「字典长什么样、代码写在哪」。

案例一:技术博客 —— 读什么、有没有读完

业务问题:哪篇文章被打开得多?用户是否滚到文末(粗略代表「读完」)?

埋点字典(节选)

事件名何时触发主要属性
page_view文章详情页展示page_type: 'article', article_id, title, author
article_scroll滚动超过 25% / 50% / 75% / 100%(各报一次,去重)article_id, depth_percent
cta_click点击文末「转载须知」「目录」等article_id, cta_name

伪代码(滚动深度,注意去重)

const reported = new Set();
function onScroll() {
  const el = document.documentElement;
  const p = (el.scrollTop + el.clientHeight) / el.scrollHeight;
  const milestones = [0.25, 0.5, 0.75, 1];
  for (const m of milestones) {
    const key = `${articleId}_${m}`;
    if (p >= m && !reported.has(key)) {
      reported.add(key);
      track("article_scroll", { article_id: articleId, depth_percent: m * 100 });
    }
  }
}

案例二:电商活动页 —— 曝光 + 下单漏斗

业务问题:列表里哪些商品「被看到」?从看到到加购、下单是否在某一步大量流失?

漏斗事件(服务端支付成功建议以服务端为准)

步骤事件名属性示例
商品卡片进入视口product_impressionsku_id, position, activity_id
点进详情product_viewsku_id
加购add_to_cartsku_id, quantity
结算页曝光checkout_vieworder_id 或匿名会话
支付成功(建议服务端)purchaseorder_id, amount

列表曝光(IntersectionObserver,同一 SKU 只报一次曝光)

const seen = new Set();
const io = new IntersectionObserver(
  (entries) => {
    entries.forEach((e) => {
      if (!e.isIntersecting) return;
      const sku = e.target.dataset.skuId;
      if (seen.has(sku)) return;
      seen.add(sku);
      track("product_impression", {
        sku_id: sku,
        position: e.target.dataset.position,
        activity_id: ACTIVITY_ID,
      });
    });
  },
  { threshold: 0.5 }
);
document.querySelectorAll("[data-sku-id]").forEach((el) => io.observe(el));

案例三:Vue / React SPA —— 路由切换算一次「页面」

问题:只加载一个 index.html,若不在路由变化时上报,整站 PV 会严重偏低

Vue Router 3/4(概念示例)

router.afterEach((to) => {
  track("page_view", {
    path: to.path,
    name: to.name,
    query_keys: Object.keys(to.query),
  });
});

React Router 6(useLocation + useEffect

const location = useLocation();
useEffect(() => {
  track("page_view", { path: location.pathname + location.search });
}, [location]);

案例四:高频点击与关页 —— 队列 + sendBeacon

问题:活动页短时间大量点击,若每次 fetch 一次,既占连接又费电;关页时队列里若还有数据,可能发不出去

思路:内存队列,每 3 秒或满 10 条批量发送;visibilitychangehidden 时用 navigator.sendBeacon 把剩余 JSON 发到采集接口(需服务端支持 POST + text/plainapplication/json)。

const queue = [];
function track(event, props) {
  queue.push({ event, props, t: Date.now() });
  if (queue.length >= 10) flush();
}
let timer = setInterval(flush, 3000);
function flush() {
  if (!queue.length) return;
  const batch = queue.splice(0, queue.length);
  fetch("/collect", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ batch }),
    keepalive: true,
  }).catch(() => {
    queue.unshift(...batch);
  });
}
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState !== "hidden") return;
  if (!queue.length) return;
  const body = JSON.stringify({ batch: queue.splice(0, queue.length) });
  navigator.sendBeacon("/collect", new Blob([body], { type: "application/json" }));
});

说明:sendBeacon 与采集网关的 Content-Type、鉴权 要事先对齐;若不支持 Beacon,可退化为 fetch(..., { keepalive: true })


10. 小结

一套可维护的埋点方案 = 清晰的问题目标 + 统一事件模型与字典 + 代码埋点为主、配合 SPA 路由与性能手段 + 合规与治理。上文四个案例分别对应 内容阅读深度电商曝光与漏斗SPA 页面浏览批量与关页可靠性;按业务选章复用,比一次性「全埋」更容易长期迭代。

Last Updated 4/10/2026, 7:23:15 AM