是这样的,我当时做前端埋点与监控 SDK,重点不是做一个大而全的数据平台,而是做一套业务前端真正愿意接、接了以后不容易出问题的轻量 SDK。
因为埋点这件事在业务里最容易失败的地方,不是后端有没有一个分析平台,而是前端 SDK 接入体验太差。开发如果每次都要在 onClick 里手写 tracker.send,时间久了业务代码会被埋点逻辑污染;动态渲染元素、弹窗、微前端容器里还容易漏采;用户关闭页面、切换路由、弱网断网时,关键事件又容易丢。最后业务方看到的数据不可信,研发也不愿意继续维护。(2 分钟)
这个问题最早是从两类现象暴露出来的。一类是业务复盘时发现漏斗里有断点,只知道用户点过页面,却不知道关键业务动作有没有真正发生;另一类是前端排障时发现关键上下文缺失,比如接口失败发生在哪个页面、哪个租户、哪个操作之后。也就是说,问题不在于“完全没有数据”,而在于 SDK 没有把采集、上下文和上报这几件事做成稳定能力。
所以这个项目的目标很明确:做一套低侵入、高可靠、可隔离、好接入的前端 SDK。它只负责把前端发生的业务事件、曝光事件、接口异常和公共上下文稳定采回来,后面的分析平台和数据消费不是这次回答的重点。
这里有三个核心技术矛盾。
第一是采集覆盖率和业务侵入性的矛盾。如果每个埋点都让开发在 onClick 里写 tracker.send,短期能做,但长期一定会和业务逻辑耦合,误删、漏埋、重复上报都很常见。关键路径一旦缺数据,后面再补也没法还原真实用户行为。
第二是数据可靠性和浏览器生命周期的矛盾。用户点击按钮后立刻关闭页面、切换路由、进入弱网或断网,普通 XHR 很容易被浏览器 abort。对业务来说,丢的往往正是转化链路里最关键的最后一步。
第三是 SDK 能力和运行风险的矛盾。SDK 会接入业务页面,如果它体积太大、初始化太慢,或者覆写 Fetch/XHR 时出错,就可能影响主业务。所以这类 SDK 必须默认把自己当成低信任组件来设计,能旁路就旁路,能降级就降级。(4 分钟)
当时也看过几种方案。第一种是做纯编程式 SDK,让业务代码主动调用 track()。它很灵活,但侵入性强,后期误删漏埋很常见。第二种是只做无侵入自动采集,比如自动采点击、曝光和页面性能。这个接入成本低,但只能得到技术事件,很难表达“提交审批”“创建客户”这种业务语义。第三种是完全交给业务组件封装,把埋点写进组件库里。这个能覆盖一部分场景,但组件一变、页面一动态组合,采集边界还是不稳定。
所以最后我选择的是“声明式采集 + 可靠上报 + 运行隔离”的 SDK 架构。业务只声明事件语义,SDK 负责事件发现、上下文补齐、队列上报、失败重试和自身降级。(6 分钟)
第一层是声明式采集。前端开发不需要在业务函数里调用 SDK,只需要在 DOM 或组件配置上声明类似 data-track-event="submit_form" 的属性。SDK 初始化时在 document 或应用 root 上做事件委托,点击事件冒泡上来后,通过 event.composedPath() 找到最近的 track 声明,再结合当前 URL、用户、租户、页面上下文和业务扩展字段组装事件。这有点类似 React 合成事件的思路,不是在每个节点上绑监听器,而是在顶层统一接管。
这里我刻意强调“声明式业务语义”,不是简单记录“点击了哪个按钮”。因为 UI 会变,按钮文案和 DOM 结构会变,但“提交审批”“创建客户”“支付成功”这些业务语义应该尽量稳定。SDK 只要求业务声明这个语义,不要求业务自己拼上报 payload。
第二层是曝光和上下文采集。对于长列表曝光,我不会用高频 scroll 监听,而是用 IntersectionObserver 做可见性判断,再配合去重集合和批量队列,避免频繁计算影响主线程。SDK 还会自动补齐公共上下文,比如页面路径、来源、用户、租户、会话、应用版本、实验桶。这样接入方只关心“这是什么事件”,不用每次重复传一堆公共字段。
第三层是可靠上报。页面正常运行时,SDK 会先进入内存队列,按批次上报,避免每次点击都打一个请求。页面卸载时,优先用 navigator.sendBeacon 发送小体积关键事件,因为它更适合在 unload 场景交给浏览器异步处理。如果 Beacon 不可用、payload 超限,或者安全策略阻止,就降级到 fetch 的 keepalive,再不行就进入本地离线队列。
弱网和断网场景下,SDK 会把事件写入 IndexedDB,并记录重试次数、创建时间和幂等 id。网络恢复后,SDK 批量续传,并使用退避重试和批量限流,避免恢复瞬间把本地积压事件一次性打出去。这里我主要关注的是 SDK 侧的队列、幂等和节流,服务端怎么消费这些事件是另一个话题。(9 分钟)
第四层是运行隔离和接口监控。SDK 会代理 Fetch 和 XHR,采集接口耗时、状态码、错误类型和页面上下文,但这里最重要的不是多采数据,而是不能改变原请求行为。比如原来的 Promise 链、headers、body、AbortController、错误抛出语义都不能被破坏。所有监控逻辑都要包在 try-catch 和旁路分支里,SDK 自己出错只能丢监控,不能拖垮业务。
这套方案也有几个必须控制的边界。动态渲染元素频繁销毁时,全局事件委托要避免漏采和内存泄漏;微前端和 iframe 场景下,要处理事件路径和上下文穿透;Beacon 有 payload 限制,不能把大事件全塞进去;覆写 Fetch/XHR 时要保持原生行为,包括 Promise、headers、body stream 和 abort;SDK 包体积要小,初始化要晚于关键渲染路径,不能拖慢首屏。(12 分钟)
最终它带来的业务价值,不是我做了一个数据平台,而是把前端采集这件事变成了一个稳定、低侵入的 SDK 能力。开发接新埋点时,不需要在业务函数里散落 tracker.send;动态页面和长列表更不容易漏采;弱网和卸载场景下关键事件有队列和降级兜底;SDK 自己出问题时也有开关和旁路,不影响主业务。
如果面试官问“效果怎么量”,我会按 目标、边界、证据、价值 只从 SDK 侧回答。第一看接入成本,新埋点是不是主要通过声明完成,而不是改业务函数;第二看漏采率,动态渲染、曝光、卸载和弱网场景要单独做回放测试;第三看运行风险,SDK 异常时是否能通过开关降级、旁路逻辑和错误隔离保证业务不受影响;第四看包体和初始化时机,不能为了采集能力牺牲首屏性能。
AI 辅助这块,我会讲得比较克制。SDK 这种基础设施不能直接信 AI 生成的代码,但 AI 可以辅助枚举兼容性风险,比如 Fetch/XHR 代理、Beacon 降级、IndexedDB 异常、不同浏览器生命周期事件。最终还是要用真实浏览器矩阵、弱网模拟、卸载场景和回放测试去验证。这样能体现 AI 协作能力,也能体现质量控制意识。
从全流工程师角度看,这个 SDK 不是为了多采一些点击数据,而是让上线后的真实用户行为、异常上下文和业务转化反馈,能够重新回流到产品、研发、测试和运营决策里。
所以我把埋点从零散的 track() 调用,重新建模成业务事件、曝光事件、异常事件和公共上下文。开发只声明业务语义,SDK 负责采集、补齐上下文、可靠上报和失败降级;测试可以围绕动态 DOM、弱网、卸载、iframe、Beacon 超限这些场景做回放验证;运营和产品拿到的数据也不是孤立 click log,而是能和页面、租户、版本、会话、接口异常关联起来的行为证据。
这里体现的全流能力,是我没有把埋点当成一个前端工具函数,而是把它作为上线运营反馈链路的一部分来设计。编码阶段如果不考虑观测和恢复,线上真正需要定位问题时,知识流就会在用户现场断掉。
如果用一句话总结,我做的是一个低侵入、高可靠、可降级的前端埋点与监控 SDK,而不是大而全的数据平台。这个方案自然会引出几个深挖点,比如 声明式埋点如何处理动态渲染元素,sendBeacon 失败时怎么降级,SDK 劫持 Fetch/XHR 会不会影响主业务,SDK 自身体积和加载时机怎么控制,以及 微前端或 iframe 场景下如何做上下文穿透。(15 分钟)
- 核心防御与深挖靶场 (面试官可能的追问)
在 15 分钟的面谈讲完上述框架后,面试官极大概率会顺着你的诱饵进行深挖。我已经为你准备好了以下 5 个独立的高频防御阵地(点击跳转复习):
- 🛡️ 事件机制防线(前端):声明式埋点如何处理动态渲染元素的事件绑定 (React/Vue 中组件频繁销毁重建,全局事件委托会不会漏采或者内存泄漏?)
- 🛡️ 高可靠通信防线(前端):sendBeacon 失败时如何降级兜底 (如果浏览器因为 payload 太大或者安全策略禁用了 sendBeacon 怎么保证数据不丢?)
- 🛡️ 系统托底防线(前端):SDK 劫持 Fetch/XHR 会不会影响主业务请求 (覆写原生的 Fetch/XHR 如果你的 SDK 本身出 Bug 报错了,会不会把整个业务页面搞崩?)
- 🛡️ 性能与打包防线(前端):埋点 SDK 的体积控制与按需加载策略 (作为一个所有业务都要接入的底层 SDK,如何保证自身的包体积足够小且不影响首屏加载性能?)
- 🛡️ 跨上下文接入防线(前端):微前端和 iframe 场景下如何串联上下文 (微前端、iframe 或弹窗里触发的事件,SDK 如何拿到正确的用户、路由和会话上下文?)
Authroute 中,请求用户接口,看一下是否登陆