GrowingIO 的无埋点并不是完全零埋点,而是把埋点动作从前端代码硬编码推迟到可视化圈选阶段。页面加载后,JS SDK 会在文档节点上注册全局事件监听,自动捕获点击、输入、表单提交、页面变化等行为,并给每个被点击的元素计算一个稳定路径。圈选时,运营或产品人员只需要在页面上点选元素,平台就会生成规则,服务端再把这些原始日志翻译成结构化事件。所以接入阶段往往只需要引入一段脚本并初始化,真正的活出现在数据口径设计环节。

一、SDK如何做到自动采集交互
GrowingIO Web SDK 的自动采集依赖浏览器事件模型。通常在 document 上以捕获阶段注册 click、change、submit 等监听器。捕获阶段意味着事件从窗口向目标元素传播时,SDK 会先于大多数业务逻辑收到事件,即使某个组件调用了 stopPropagation,采集仍有机会完成。监听器触发后,SDK 通过 event.target 拿到真正被点击的 DOM 节点,再向上递归计算路径,同时读取节点文本、属性、页面 URL、会话 ID 等信息。
元素路径的稳定性是无埋点的关键。如果直接记录 DOM 索引,比如 div:nth-child(3) > button,页面结构一旦调整,事件规则就会失效。GrowingIO 会综合 id、class、data-gio-*、标签名、文本片段等生成选择器,同时允许前端通过自定义属性固化标识。例如电商列表页的每个商品卡片的购买按钮,可以通过 data-gio-track='buy' 来统一标记,避免平台为每个商品都生成一条规则。
接入脚本通常是异步加载,避免阻塞页面渲染。初始化时建议显式传入 version,这样同一工程在不同迭代中产生的数据可以隔离。debug 模式会把采集日志输出到控制台,方便排查元素是否被捕获。下面的示例展示了标准初始化流程。
(function(e,t,n,g,i){
e[g]=e[g]||[];
var m=t.createElement(n),s=t.getElementsByTagName(n)[0];
m.async=1;
m.src='https://assets.growingio.com/sdk/gio.js';
s.parentNode.insertBefore(m,s);
})(window,document,'script','gio');
gio('init', 'your-project-id', {
version: '1.0.0',
debug: false,
dataCollect: true,
hashtag: true
});
gio('send');
初始化的第三个参数可以控制采集细节。hashtag 开启后,URL 中 # 后面的路由变化也会作为页面访问事件;dataCollect 控制是否上报用户行为。需要特别注意的是,SDK 默认不会采集密码输入框的内容,但其他输入的 value 是否上报、如何脱敏,应当在初始化阶段与合规方确认。若表单里包含手机号、身份证号,可以在平台配置字段级脱敏规则,或关闭对应输入事件的自动采集。
二、从自动采集日志到可用事件
原始日志只是一堆带路径的点击流,真正形成业务指标需要经过圈选。进入 GrowingIO 的管理后台,打开圈选模式后,页面会与当前账号建立临时连接。鼠标悬停在任一元素上时,后台会反查出该元素的路径,生成候选规则。点击确认后,规则会保存为事件。之后每一条命中该路径的点击日志,会被后端归并到该事件下,而不再是一条无意义的原始点击。
圈选规则本质上是一种元素匹配表达式。它可以是简单的 CSS 选择器,也可以带上父级约束、文本匹配、属性包含等条件。一个设计良好的规则应当兼顾覆盖范围与精度。比如商品列表页有 20 个相同结构的卡片,应当圈选其中一个购买按钮,再通过通配或自定义属性把同类按钮全部纳入;如果直接圈选了某个具体位置,数据就会漏采。平台通常提供规则测试功能,可以在保存前查看过去一段时间内有多少日志能命中该规则。
无埋点适合点击类、页面访问类、表单提交类事件的采集,但在一些场景下需要通过代码补充。比如某个重要转化发生在异步请求成功后,界面并没有变化,自动采集就看不到;又比如虚拟页面的概念,当单页应用路由切换后,虽然 URL 变了,平台可能仍按首次加载处理,这时可以手动补发页面浏览事件。下面的代码演示了自定义事件与用户身份的关联。
// 设置登录用户ID
gio('setUserId', 'user_88231');
// 上报自定义事件,补充业务属性
gio('track', 'order_submit', {
order_id: 'SO20250811001',
pay_type: 'wechat',
amount: 129.00,
product_count: 2
});
// 手动补报一个虚拟页面浏览
gio('track', 'pageview', {
page_name: 'order_success'
});
自定义事件的命名最好统一小写加下划线,属性值避免塞入不可枚举的自由文本。像订单金额、支付方式、商品 ID 这类结构化字段,建议显式传给 track 函数;而商品标题、备注等长文本则不要作为大基数属性上报,否则会影响后续的分组计算效率。用户登录后要及时调用 setUserId,将匿名行为与会话拼接起来,否则在跨端或登录前后漏斗分析中会出现断层。
三、数据上报链路与指标计算
采集到的日志不会一条请求一条请求地发送,那样会消耗大量连接。SDK 会在内存中缓存事件,按固定条数或固定间隔批量上报,并在页面隐藏或卸载前尝试 flush。批量请求到达 GrowingIO 接收端后,会先经过格式校验、字段补全、规则匹配,再写入列式存储。这样设计有两个好处:一是减少网络开销,二是服务端可以在规则变更后重新计算历史事件,而不用等待新数据。
指标计算的入口是一张统一的事件表。每个事件至少包含事件 ID、事件类型、触发时间、会话 ID、页面路径、元素路径、业务属性。PV 和 UV 基于页面访问事件去重,点击率用点击次数除以曝光次数,漏斗则需要按会话或用户分组后,判断是否在设定顺序内依次完成。热图更依赖点击事件的坐标与元素路径聚合,前端圈选时记录的元素位置会与这些坐标匹配。
{
"event_type": "click",
"element_path": "body > div#app > button.submit-btn",
"element_text": "提交订单",
"page_url": "https://shop.ipipp.com/checkout",
"session_id": "s_09d3f2...",
"timestamp": 1754899200000,
"attributes": {
"data_gio_id": "submit_order"
}
}
无埋点带来的一个常见问题是数据膨胀。因为自动采集记录了大量非关键点击,如果不在规则层过滤,事件表会迅速变大。尤其在长列表、热力图中,几万行日志可能几分钟就产生。建议在初始化时关闭不需要的采集类型,比如只采集点击和页面访问,不采集 change;对测试环境单独创建项目,避免埋点数据混入生产。服务端还支持按照 IP、User-Agent、自定义维度排除流量,帮助提高数据准确性。
四、无埋点与手工埋点如何配合
无埋点方案最擅长的是探索式分析,因为历史行为已经留存,后续有新分析需求时,直接圈选即可回溯数据,不需要修改代码重新发版。但当业务指标非常稳定、口径复杂、且需要跨多端精确一致时,手工埋点仍然不可替代。例如电商的支付成功事件,通常由服务端通知或客户端在明确结果回调里触发,而不是简单依赖点击支付按钮,因为用户可能在支付渠道页面取消或失败。
实际项目中比较合理的做法是:高频、结构化、有复杂业务语义的操作使用手工埋点,通过 track 显式上报;长尾、探索性、交互类行为交给无埋点。两者在 GrowingIO 中可以进入同一个分析体系,只是事件来源不同。手工埋点事件往往带更多可靠的业务属性,无埋点事件则补充界面层面的行为细节,例如用户查看了哪些商品、从哪个入口进入等。
接入后还需要建立事件字典,避免事件命名和属性含义在不同页面、不同负责人之间产生歧义。例如 buy_click 到底指购物车点击、商品卡片点击,还是提交订单按钮点击,必须写清口径。无埋点的好处是可以在平台上直接查看元素截图和路径,辅助确认事件定义。最后,建议定期清理失效规则和无效事件,保持事件表可维护。因为无埋点项目中最容易积累大量只使用过一次的临时事件,它们会拖慢报表加载并干扰分析。