Skip to content

初始化 ​

接入只有两步:构造实例、await 初始化。

ts
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 之后再读取这些成员或注入请求头:

ts
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,渠道参数已丢失,归因随之失败。

ts
// ❌ 错误: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 下两处均为空串),作为会话的入口锚点。

ts
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 为空对象 {}。

ts
// 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 错误事件,走同一条上报管线。
ts
// 客户端:打开 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() 完成后,最直接的确认方式:

  1. 读 device id:await trace.init() 后 trace.deviceId 应为非空字符串。
  2. 看上报请求:带渠道参数访问(?utm_source=baidu),浏览器 Network 面板应有发往 /v2/trace/events 的请求。
  3. 挂回调观察:构造时传 onReported,事件上报后会触发:
ts
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 成功

下一步 ​

基于 MIT 协议发布(内部使用)