Skip to content

快速上手 ​

@tripo3d/trace 的核心 API 只有三步:new TripoTrace(params) 创建实例,await trace.init() 初始化 device id,trace.landing() 触发归因上报。

接入位置很关键

SDK 在 new TripoTrace(...) 时会立即缓存 window.location.href 作为 entry URL, 后续 landing() 用的是这个缓存值。务必在「业务任何 URL 操作(清理 query、SPA 路由跳转、history.replaceState 等)之前」完成 new。Nuxt 接入推荐使用 05.trace.client.ts 这样的早序号 plugin。

完整流程 ​

ts
import { TripoTrace } from '@tripo3d/trace';

const trace = new TripoTrace({
  baseUrl:   'https://api-cn-test.tripo3d.com',
  configUrl: 'https://cdn.example.com/trace/whitelist.json',
  onError:   (err, ctx) => console.warn('[trace]', ctx, err),
});

// 1. 初始化 device id(首次:从 fingerprint 现算并写 localStorage;后续:直接读缓存)
// 业务必须 await,完成后 trace.deviceId / trace.headers 即可用
await trace.init();

// 2. 触发归因上报(fire-and-forget,不阻塞业务)
// 读 entry URL 的 utm_source → 拉取 CDN 白名单 → 命中则 POST landing
trace.landing();

为什么 init 是异步的

device id 来自 FP.load() —— 内部用 requestIdleCallback 等一帧空闲,保证 Canvas/WebGL 等特征渲染稳定。这一步只在首次访问时跑一次,之后命中 localStorage 缓存。

常见错误:忘记 await init()

ts
trace.init();        // ❌ 没有 await
trace.landing();     // ❌ 此时 deviceId 还是空字符串,会被静默跳过

正确写法:

ts
await trace.init();  // ✅ 等待 deviceId 就绪
trace.landing();     // ✅ 正常上报

landing() 在 deviceId 为空时会静默 return,不会报错也不会上报,排查时注意检查调用时序。

TripoTraceParams ​

字段类型 / 默认说明
baseUrlstring(必填)归因服务 base URL,例如 https://api-cn-test.tripo3d.com
configUrlstring(必填)CDN 白名单 JSON 的完整 URL;JSON 契约见下方 §CDN 白名单配置
deviceIdOverridestring业务自管 device id 时显式传入,跳过指纹计算
fetchtypeof fetch / globalThis.fetch注入自定义 fetch,Nuxt 项目可传 $fetch;同时用于 CDN 配置与 landing 上报
onError(err, ctx) => void错误回调;ctx.phase 是 init / config / report,便于定位
onReport(payload) => void上报成功钩子,便于业务做埋点链路监控

实例属性 / 方法 ​

名称签名说明
trace.deviceIdstring当前 device id;未 init 或 SSR 下为 ''
trace.headersRecord<string, string>{ 'x-tripo-device-id': string };无 id 时为 {},安全合并
trace.init()() => Promise<void>初始化 device id;SSR 下 no-op。业务必须 await
trace.landing()() => Promise<void>触发归因上报。必须在 init() resolve 之后调用,否则会被静默跳过(不上报且不报错)

没有公开的 reportLanding

归因上报是 SDK 内部行为,不暴露给调用方。业务方需要做的只有 new + await init() + landing() 三步。

CDN 白名单配置 ​

CDN 上托管一份 JSON,schema 如下:

json
{
  "version": 1,
  "killSwitch": false,
  "utm_source": ["baidu", "google", "bytedance"]
}

字段含义:

字段类型说明
versionnumberschema 版本。SDK 仅识别 1;未来不兼容升级时会改成 2,旧 SDK 自动 fail-open
killSwitchboolean全局熔断。true 时 SDK 完全不上报,用于线上紧急止血(不发版即可生效)
utm_sourcestring[]utm_source 白名单。值统一小写;SDK 端会先把 URL 上的 utm_source 转小写再比对

字段名为什么用 UTM 全称

未来可能扩展 utm_medium / utm_campaign 等同结构白名单,全称字段名可以避免歧义、 方便 SDK 端循环遍历。

CDN 部署建议:

  • 响应头 Cache-Control: max-age=300, public(5 分钟),权衡命中率与生效速度。
  • 配置 CORS 允许业务域名访问(GET)。
  • 出问题时只需把 JSON 改为 "killSwitch": true 上传 CDN(购买几分钟生效时间)。

fail-open 行为 ​

CDN 拉取失败 / JSON 字段不合法 / version 不匹配时,SDK 不会因此放弃上报, 而是 fail-open:只要 URL 带 utm_source 就继续上报。这样可以避免 CDN 故障导致 投放数据全丢;后续可在数据后处理阶段二次过滤。

CDN 配置 killSwitch: true 是显式熔断,与 fail-open 是两回事:killSwitch 为 true 时 SDK 完全不上报。

Nuxt 接入 ​

步骤 1:runtimeConfig ​

ts
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      attributionBaseUrl:   'https://api-cn-test.tripo3d.com',
      attributionConfigUrl: 'https://cdn.example.com/trace/whitelist.json',
    },
  },
});

步骤 2:客户端 plugin ​

app/plugins/05.trace.client.ts(.client 后缀让 Nuxt 仅在客户端执行,避免 SSR 阶段执行 fingerprint;05. 序号靠前,保证在业务任何 URL 操作之前完成 new):

ts
import { TripoTrace } from '@tripo3d/trace';

export default defineNuxtPlugin(async (nuxtApp) => {
  const config = useRuntimeConfig();

  const trace = new TripoTrace({
    baseUrl:   config.public.attributionBaseUrl   as string,
    configUrl: config.public.attributionConfigUrl as string,
    onError:   (err, ctx) => console.warn('[trace]', ctx, err),
  });

  // 初始化 device id
  await trace.init();

  // 触发归因上报(fire-and-forget)
  trace.landing();

  // SPA 路由变化时再触发一次(1 分钟窗口内去重,超窗后同一 URL 会再次上报)
  const router = useRouter();
  router.afterEach(() => {
    trace.landing();
  });

  return {
    provide: { trace },
  };
});

步骤 3:业务请求拦截器 ​

ts
// composables/useApi.ts 或 plugins/api.ts
export const apiFetch = $fetch.create({
  baseURL: useRuntimeConfig().public.apiBase,
  onRequest({ options }) {
    if (import.meta.client) {
      const { $trace } = useNuxtApp();
      Object.assign(options.headers ||= {}, $trace.headers);
    }
  },
});

后续所有用 apiFetch(...) 发出的请求都会自动带上 x-tripo-device-id。

SSR 行为 ​

ts
// SSR 阶段(isBrowser === false)
const trace = new TripoTrace({ baseUrl: '...', configUrl: '...' }); // ✅ SSR 下不读 window,安全
await trace.init();                                                  // ✅ 立即 resolve(no-op)
trace.landing();                                                     // ✅ 立即 return(no-op)
trace.deviceId                                                       // === ''
trace.headers                                                        // === {}

业务侧用 .client.ts plugin 后缀就能保证只在客户端执行;即便忘加,库也不会爆错。

下一步 ​

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