初始化
接入只有两步:构造实例、await 初始化。
import { TripoLens } from '@tripo3d/lens';
const trace = new TripoLens({
app: 'tripo',
configUrl: 'https://cdn.example.com/trace/whitelist.json',
endpoint: 'https://your-gateway.example.com/v2/trace/events',
});
await trace.init();构造做的是「定参数、快照入口 URL、登记插件」等同步、零副作用的工作;真正的解析、装配、首访归因上报都在 init() 里。
init() 内部做了什么
调用 init() 后,SDK 按固定顺序执行(源于 TripoLens.init()):
init()
├─ ① 非浏览器(SSR)→ 直接 return(no-op)
├─ ② 解析 device id override → cookie → localStorage 缓存 → FingerprintJS 现算(再写缓存)
├─ ③ 生成 session id 每次页面加载新生成
├─ ④ 载入 Consent 读本地授权状态,无则用合规默认(当前默认全开)
├─ ⑤ 拉取远程配置 有界 3s 超时、永不抛;失败按 缓存 → 内置默认 降级
├─ ⑥ 解析 Debug 桥 仅 debug 开启时动态加载 WindowBridgeTap(默认不进主包初始 bundle)
├─ ⑦ 装配 L4 Pipeline + L5 传输栈(用 ⑤ 的配置快照)
├─ ⑧ setup 全部插件 默认 attribution/identity + 已授权的 opt-in 插件
│ (AttributionPlugin 在此读 entry URL 快照判定 utm_source → 命中即上报 landing)
└─ ⑨ 非 killSwitch 时 restore:补发上次未发完的行为事件必须 await init()
init() 是异步的,内部要做两件耗时且互相独立的事:
- 解析 device id:override → cookie → localStorage 缓存 → FingerprintJS 现算(再写缓存,cookie 为主、localStorage 兜底,详见 身份与会话)。
trace.deviceId/trace.headers要等这一步完成才有值。 - 拉取远程配置:3 秒有界超时、永不抛错,慢 / 挂死的 CDN 也不会拖死它(fail-open,详见 远程配置);供随后装配插件读取曝光策略、错误分级、采样率等快照。
因此在 init() resolve 之前,trace.deviceId 为空串 ''(device id 尚未解析)、trace.headers 为空对象 {},归因也不会上报。务必 await 之后再读取这些成员或注入请求头:
trace.init(); // ❌ 没 await
trace.headers // ❌ 此刻还是 {}
await trace.init(); // ✅
trace.headers // ✅ { 'X-DeviceId': '...' }entry URL 快照:为什么必须最早构造
new TripoLens(...) 时会立即快照 window.location.href 作为入口 URL,归因后续判定 utm_source 用的是这个快照值,而不是上报那一刻的实时 URL。
务必在任何 URL 操作之前构造
若业务先清理了 query(去掉 UTM 参数)、做了 SPA 路由跳转或 history.replaceState,再去 new TripoLens,快照到的就是被改过的 URL,渠道参数已丢失,归因随之失败。
// ❌ 错误:UTM 已被业务清掉,再构造就晚了
cleanUrlQuery();
const trace = new TripoLens({ app, configUrl, endpoint });
// ✅ 正确:进入页面第一时间构造,先快照住带 UTM 的入口 URL
const trace = new TripoLens({ app, configUrl, endpoint });
cleanUrlQuery(); // 之后随便改 URL 都不影响归因
await trace.init();这个快照可通过只读属性 trace.entryUrl 直接获取;它在构造完成后立即可用,不需要等待 init(),且不随后续 SPA 路由变化。快照同时会作为公参 context.entry_url 注入每条上报事件(整会话固定,SSR 下两处均为空串),作为会话的入口锚点。
const trace = new TripoLens({ app, configUrl, endpoint });
trace.entryUrl; // 例如 'https://example.com/landing?utm_source=google'SSR 行为
服务端是否真正采集数据,取决于有没有接入 SSR 采集(SSR Part B)。两种情况下 init() 的行为不同——但有一条始终成立:主入口 init() 在服务端永远是 no-op,绝不在 Node 侧采集或上报。
未接入 SSR 采集(默认)
在非浏览器环境(Node SSR、构建期)下,主入口 import 安全、构造安全,init() 立即 resolve 且不产生任何副作用(不解析指纹、不拉配置、不上报)。此时与「未 await」时的空态一致:trace.entryUrl / trace.deviceId 为空串 ''、trace.headers 为空对象 {}。
// Node / 构建期
const trace = new TripoLens({ app: 'tripo', configUrl: '...', endpoint: '...' }); // ✅ 不读 window
await trace.init(); // ✅ 立即 resolve(no-op)
trace.entryUrl // === ''
trace.deviceId // === ''
trace.headers // === {}到了浏览器,客户端 init() 才正常解析 device id、装配插件、上报。「import 不爆错」不等于「采集了 SSR 数据」——主入口在服务端全程 no-op,原理见 架构总览 · SSR 安全。
接入了 SSR 采集
要在服务端真正采集渲染期的错误 / 性能,需走 SSR Part B(SSR 采集)。就 init() 而言,记住两侧行为不同:
- 服务端:采集由独立的
@tripo3d/lens/server(仅 Node)原语完成——SsrPerfCollector/SsrErrorBuffer采集、serializeSsrContainers把容器注入初始 HTML,全程不经过trace.init();主入口init()在服务端仍是 no-op。 - 客户端:插件打开
captureSsr后,init()在正常流程之外额外一次性读取注入的容器(window.__TRIPO_SSR_PERF__/__TRIPO_SSR_ERR__,读后清空),展开成标准performance/ssr错误事件,走同一条上报管线。
// 客户端:打开 captureSsr,init() 注水后顺带读容器并上报
const trace = new TripoLens({
app: 'shop',
configUrl: '...',
endpoint: '...',
plugins: [
new ErrorPlugin({ captureSsr: true }), // 默认关,需服务端先注入错误容器
new PerformancePlugin({ captureSsr: true }), // 默认关,需服务端先注入性能容器
],
});
await trace.init(); // 客户端正常 init + 读取 SSR 容器并上报客户端接线如上(所有环境通用);服务端接线按框架不同——Nuxt 用 @tripo3d/lens/nuxt 自动完成,通用 Node(Express / Koa 等)手动用 @tripo3d/lens/server 接入。完整两侧步骤与各环境代码见 SSR 采集 · 服务端按环境接线。
框架接入
各框架优先用对应适配器,零接线接入声明式埋点(及 Nuxt 的 SSR 采集),不必手写上面的接线:
- Vue 接入 ——
v-trace指令 - React 接入 ——
useTraceProps/<Trace> - Nuxt 接入 —— Nuxt Module,
nuxt.config配三个必填项(app/configUrl/endpoint)即零接线,含客户端 + SSR 自动接线
适配器「不构造实例、需自己传入」的约定,以及不便用适配器时的手动接入示例,见各适配器页。手动接入的通用要点:用 .client 早序号 plugin 只在客户端执行,并在任何 URL 操作之前构造。
生命周期方法
| 方法 | 说明 |
|---|---|
init() | 见上。业务必须 await;幂等,重复 / 并发调用复用同一 promise;SSR 下 no-op。 |
flush() | 立即把积压事件批量发出;未 init / SSR 下跳过。 |
destroy() | teardown 全部插件 + 销毁队列。持久化数据保留(device id 等不清)。 |
验证是否生效
init() 完成后,最直接的确认方式:
- 读 device id:
await trace.init()后trace.deviceId应为非空字符串。 - 看上报请求:带渠道参数访问(
?utm_source=baidu),浏览器 Network 面板应有发往/v2/trace/events的请求。 - 挂回调观察:构造时传
onReported,事件上报后会触发:
const trace = new TripoLens({
app: 'shop',
configUrl: '...',
endpoint: '...',
onReported: (type) => console.log('[lens] 已上报', type),
onError: (err, ctx) => console.warn('[lens]', ctx.phase, err),
});
await trace.init();
console.log('deviceId =', trace.deviceId); // 非空即 init 成功