React 接入(@tripo3d/lens/react)
在 React 里做埋点,最直接的方式是把坐标、事件、参数写成元素上的 data-trace* 字符串属性——核心的 行为埋点 · autoTrack 会在点击 / 曝光触发那一刻用 getAttribute 读取它们。这套属性是纯 DOM 契约,JSX 里用 {} 表达式就能写入,props / state 会随重渲染自动反映到属性上,不需要任何适配器。
@tripo3d/lens/react 是这套契约之上的可选 DX 层:把「多个 data-trace* 属性映射 + params 序列化 + 循环引用兜底」打包成 useTraceProps(主原语)与 <Trace>(组件糖),并经 <TraceProvider> 补上命令式逃生口 useTrace()。本文先讲不装适配器的原生写法,再讲初始化,最后才是适配器带来的便利。
先看用法:原生 JSX 写 data-trace*
声明式采集只认 DOM 上的 data-trace* 字符串属性——点击 / 曝光触发时才用 getAttribute 读取,SDK 本身不理解、也不需要理解 React 状态。所以你完全可以不装适配器,直接在 JSX 里写属性:坐标固定就用字符串字面量,需要动态就用 {} 表达式。
import { TraceEvent } from '@tripo3d/lens'; // 事件枚举:避免硬编码 'click' / 'expose'
function Demo({ sku, tab }: { sku: string; tab: string }) {
return (
<>
{/* 1. 纯静态:坐标固定,直接写字符串字面量 */}
<button data-trace-key="home.banner.buy" data-trace-type="click">
购买
</button>
{/* 2. 动态 params + 枚举多事件:用 {} 表达式,对象须自己 JSON.stringify */}
<button
data-trace-key="home.banner.buy"
data-trace-type={[TraceEvent.Click, TraceEvent.Expose].join(' ')}
data-trace-params={JSON.stringify({ sku })}
>
购买
</button>
{/* 3. 坐标拼接:祖先 data-trace 段 + 叶子 data-trace-key,动态段用 {} → home.nav.docs */}
<section data-trace="home">
<a data-trace="nav" data-trace-key={tab} data-trace-type="click">
文档
</a>
</section>
</>
);
}为什么 {} 能拿到动态值
React 的更新模型:state / props 变化触发组件重渲染,JSX 重新求值(读到当前快照值),再由 ReactDOM 在 commit 阶段 patch 到 DOM 属性;采集器在触发那一刻读属性,读到的始终是最新值。与 Vue 的区别只在内部机制(React 是整组件重渲染 + diff,而非细粒度依赖追踪),对采集端完全等价。这次没返回的 data-trace* 会随新 props 被 React diff 自动移除,无需手动清理。
要点(规则与手写 HTML 完全一致,详见 行为埋点 · 声明式埋点):
- 锚点:元素必须带
data-trace或data-trace-key才会被采集;单独写data-trace-type/data-trace-params不会触发。拼出的坐标须为合法 TracePath(2–8 段),否则静默丢弃。 - 事件:
data-trace-type缺省为click;多事件用空格分隔("click expose")。token 可从@tripo3d/lens引入TraceEvent常量避免硬编码——data-trace-type={TraceEvent.Click},多事件data-trace-type={[TraceEvent.Click, TraceEvent.Expose].join(' ')}。但请注意:JSX 里data-*属性的值类型是string,享受不到useTraceProps的on字段那样的类型约束(拼错不报错),想要类型安全请用useTraceProps/<Trace>。 - 参数:
data-trace-params是三者中唯一需要序列化的——必须传字符串,直接传对象会被写成"[object Object]"导致解析失败(见下方警告)。坐标(data-trace/data-trace-key)与事件(data-trace-type)都是字符串,直接写即可。
data-trace-params 不要直接传对象
JSX 里 data-trace-params=(外层 {} 是表达式、内层 { sku } 是对象字面量)会被 React 写成字符串 "[object Object]"(DOM 属性只能存字符串),采集端 JSON.parse 失败、参数丢失。必须写成 data-trace-params={JSON.stringify({ sku })}。另外手写 JSON.stringify 遇到循环引用会直接抛错、可能中断渲染,需自行 try/catch——useTraceProps / <Trace> 已内置这层兜底。
属性写了不等于会上报
上面的写法只是把 data-trace* 落到了 DOM。真正的点击委托与曝光登记,需要一个已初始化、且开启 autoTrack 的核心实例——见下一节。
让埋点生效:初始化核心实例
data-trace* 属性要被采集上报,前提是页面里已经 new TripoLens 并 init(),且装了 TrackingPlugin({ autoTrack: true })。这一步与「用不用适配器」无关,是所有方案的共同前提,只依赖核心包 @tripo3d/lens。
pnpm add @tripo3d/lensnpm install @tripo3d/lensyarn add @tripo3d/lensnew TripoLens() 出来就是个普通对象,与框架无关。用一个模块导出存起来,声明式采集即可全局生效,命令式 API 也随处可 import:
// lib/trace.ts
import { TripoLens, TrackingPlugin } from '@tripo3d/lens';
// 尽早构造(快照入口 URL);autoTrack 必开,声明式采集才会启动
export const trace = new TripoLens({
app: 'shop',
configUrl: 'https://cdn.example.com/lens/config.json',
endpoint: 'https://collect.example.com/report',
plugins: [new TrackingPlugin({ autoTrack: true })],
});
await trace.init();在入口 import 一次这个模块,确保它在应用挂载前执行:
// main.tsx
import { createRoot } from 'react-dom/client';
import App from './App';
import './lib/trace'; // 触发构造 + init;纯声明式项目到这里就够了
createRoot(document.getElementById('root')!).render(<App />);适配器不托管实例
与 Nuxt 适配器(模式 A 可自动创建客户端实例)不同,React 适配器只提供原语与 Provider、不构造也不 init 实例。渠道归因要求实例在业务任何 URL 操作之前构造,所以你需自己尽早 new TripoLens(快照入口 URL)——放在 lib/trace.ts 顶层并尽早 import 即可。
构造实例的必填参数
new TripoLens(params) 在构造期即校验 app / configUrl / endpoint 三项,缺任一直接抛错(无内置默认值);其余如 plugins、beforeEmit、onError、fetch 等均为可选,行为/错误/性能采集为 opt-in、需经 plugins 追加对应插件。各参数的类型、归一化与校验规则见 构造参数,初始化时序见 初始化。
埋点依赖 autoTrack
无论手写 data-trace* 还是用原语,真正的点击委托与曝光登记都由 TrackingPlugin({ autoTrack: true }) 内的声明式采集器完成。不开 autoTrack,属性会照写但无人采集。
可选:React 适配器与声明式原语
到这里,纯手写 data-trace* 已经能覆盖全部采集能力——响应式、SSR、曝光时序都不缺。适配器要解决的只是手写的重复与易错:多属性映射、params 的 JSON.stringify、循环引用兜底。装上它,useTraceProps(主原语)与 <Trace>(组件糖)替代逐属性 spread;再经 <TraceProvider> 补上命令式逃生口 useTrace()。
React 适配层已折入 core(子路径 @tripo3d/lens/react),无需额外安装。core(@tripo3d/lens)把 react(^17 || ^18 || ^19)声明为 optional peerDependency,请确保工程内已安装。用 <TraceProvider> 包裹应用根,把上一节构造的实例传入:
// main.tsx
import { createRoot } from 'react-dom/client';
import { TraceProvider } from '@tripo3d/lens/react';
import App from './App';
import { trace } from './lib/trace'; // 上一节构造好的实例
createRoot(document.getElementById('root')!).render(
<TraceProvider trace={trace}>
<App />
</TraceProvider>,
);用原语声明式埋点
两种写法,绑定字段与 Vue 一致(key / path / on / params / exposePolicy):
import { useTraceProps, Trace, TraceEvent } from '@tripo3d/lens/react';
function Demo({ sku, tab }: { sku: string; tab: string }) {
return (
<Trace as="section" path="home">
{/* 主原语:spread 到任意元素,重渲染自动反映 params 变化 */}
<button
{
...useTraceProps(
{
key: 'buy',
on: 'click',
params: { sku }
})
}
>
购买
</button>
{/* 组件糖:as 指定元素;叶子末段用 traceKey(key 是 React 保留 prop) */}
<Trace
as="button"
traceKey="card"
on={
[TraceEvent.Click, TraceEvent.Expose]
}
params={
{ sku }
}
>
卡片
</Trace>
{/* 独立扁平 path */}
<Trace
as="a"
path={`home.nav.${tab}`}
on={
[TraceEvent.Click, TraceEvent.Expose]
}
>
文档
</Trace>
</Trace>
);
}与 Vue 的两处差异
- 叶子末段:
useTraceProps用key;但<Trace>因 React 保留keyprop,改用**traceKey**。 - 属性清理:无需 Vue 指令那套
removeAttribute——React 重渲染丢弃不再返回的 props,旧data-trace* 自然移除。
useTraceProps(binding) 的绑定字段(key / path / on / params / exposePolicy)与 Vue 接入 同。 <Trace> 组件属性:
| prop | 映射 | 说明 |
|---|---|---|
as | — | 渲染的元素 / 组件,默认 'div' |
traceKey | data-trace-key | 叶子末段(避开 React 保留的 key) |
path | data-trace | 扁平坐标或祖先段 |
on | data-trace-type | 触发事件,缺省 click;可数组多事件 |
params | data-trace-params | 上报参数 |
exposePolicy | data-trace-expose-policy | 元素级曝光去重策略,用 ExposePolicy 枚举取值(如 ExposePolicy.OncePerElement 列表项各报一次) |
其余透传 prop 照常工作,且 data-trace* 后铺,不会被业务 prop 覆盖。 坐标规则详见 行为埋点 · 声明式埋点。ExposePolicy 枚举与 TraceEvent 一样由 @tripo3d/lens/react re-export(也可从 @tripo3d/lens 导入)。
手写 vs 原语:该用哪种
| 场景 | 推荐 |
|---|---|
| 坐标固定、无动态参数 | 直接写静态 data-trace*,最轻 |
有动态 params / 动态坐标段 | useTraceProps / <Trace>(自动 JSON.stringify + 循环引用兜底 + 多属性映射);或手写 {} + JSON.stringify |
想少写重复、避免 [object Object] 坑 | useTraceProps / <Trace> |
简言之:手写 data-trace*(含 {} 表达式)在能力上等价于 useTraceProps / <Trace>,响应式、SSR、曝光时序都不缺;区别只在 DX——原语把「多属性映射 + params 序列化 + 循环引用兜底」打包掉,动态参数场景更省心、更不易错。
纯 HTML / 原生 JS 场景
不在 React 里(服务端模板、静态站、原生 JS)也能用同一套 data-trace*:直接写属性、用 setAttribute / dataset 动态更新即可,用法见 行为埋点 · 声明式埋点。
命令式埋点
命令式 API(track / identify / captureException …)都挂在 TripoLens 实例上, 所以分两步:先拿到实例,再调用。
拿到实例的两种方式
按项目形态选其一。
路径一:自己管实例(最简,CSR 首选)
上一节的 lib/trace.ts 导出的实例与框架无关,哪儿都能 import——纯函数、组件、工具库都行:
// 任意文件直接调用,无需 Hook、无需 Provider
import { trace } from '@/lib/trace';
// 上报标记为 track 类型
trace.track('pay.done', { orderId });
// 需要其他上报类型
trace.click('pay.button');
trace.expo('pay.button');这条路完全不经过适配器:既不用 <TraceProvider>,声明式 useTraceProps / <Trace> 也照常工作(只写 data-trace* 属性、采集靠核心 autoTrack,本就不需要实例)。 换句话说——手动初始化后实例就已经全局可用,用户自管即可。
路径二:交给 Provider 注入(要逃生口 / DI / SSR 时)
用 <TraceProvider> 包裹应用根传入实例(接入示例见上文 可选:React 适配器与声明式原语), 组件内用 useTrace() 拿:
<TraceProvider trace={trace}>
<App />
</TraceProvider>仅在以下情况才需要走这条路:
| 需求 | 为什么要把实例传给 Provider |
|---|---|
| 框架风格逃生口 | 不想在组件里硬 import 模块变量,改用 useTrace() 从 Context 拿 |
为什么 React 是 Provider,而 Vue 是 app.use
两者对等,都只做一件事——把实例放进框架的依赖注入体系,好让组件内的逃生口拿到。 区别只在 API 形态:Vue 有 app.use(plugin, options) 全局插件机制,根注入一次即可; React 没有等价的全局机制,跨层级共享实例的标准做法是 Context,所以用 <TraceProvider> 包裹应用根——子树内 useTrace() 都能读到(读不到则抛错)。 Provider 还顺带注入 onError(params 序列化失败回调),声明式原语也从同一 Context 读取它。
Provider 并非采集前提
Provider 只是命令式逃生口与 onError 的接线点。 声明式属性的采集由核心 autoTrack 完成,挂不挂 Provider 都不影响——纯声明式项目可完全不用 <TraceProvider>(走上面的路径一即可)。
调用命令式 API
走路径二注入后,组件内用 useTrace() 拿到实例上报:
import { useTrace } from '@tripo3d/lens/react';
function CheckoutButton() {
const trace = useTrace(); // 未挂 Provider 或未传 trace 时抛错
return (
<button
onClick={
() => trace.track('checkout.submit', { amount: 99 })
}
>
结算
</button>
);
}useTrace() 返回完整 TripoLens 实例
拿到的不只是 track——可调用任意命令式 API: identify / clearUser(见 身份与会话)、 trackImpression / setExposurePolicy(曝光进阶,见 行为埋点 · 进阶)、 captureException / captureMessage(见 错误收集)、 startTransaction / markHydration(见 性能收集)、 setGlobalParams、updateConsent(见 Consent 授权)等。
命令式方法的语义见 行为埋点 · 命令式埋点。
SSR
data-trace* 是普通 props,React 服务端渲染即写进 HTML,hydration 后即可被采集,无需额外接线。服务端采集(性能 / 错误)机制见 SSR 采集。
Provider 选项
<TraceProvider>:
| prop | 类型 | 默认 | 说明 |
|---|---|---|---|
trace | TripoLens? | — | 已构造(建议已 init)的实例;提供后才有 useTrace() |
onError | (err) => void | — | params 序列化失败回调(坐标仍保留,仅丢 params) |
故障排查
| 症状 | 原因 | 处理 |
|---|---|---|
| 埋点元素不上报,控制台无报错 | 没开 autoTrack,属性写了但无人采集 | 构造时加 new TrackingPlugin({ autoTrack: true }) |
useTrace() 抛错 | 未挂 <TraceProvider>,或未传 trace | 用 <TraceProvider trace={...}> 包裹应用根 |
<Trace> 的 key 不生效 | key 是 React 保留 prop,读不到组件内 | 叶子末段改用 traceKey |
| 点击有上报、曝光没有 | 曝光阈值 / 去重策略 | 见 曝光去重策略 与 AutoTrackOptions.exposeThreshold |