高级用法
CDN 白名单配置文件维护
SDK 启动时会从 configUrl 拉取一份 JSON,判定 URL 上的 utm_source 是否应该上报。 JSON 由运营 / 数据团队维护,改完上传 CDN 即生效,无需发版。
Schema
{
"version": 1,
"killSwitch": false,
"utm_source": ["baidu", "google", "bytedance"]
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
version | number | 是 | SDK 仅识别 1,非 1 将 fail-open |
killSwitch | boolean | 是 | 全局熔断;true 时 SDK 完全不上报 |
utm_source | string[] | 是 | utm_source 白名单(值统一小写) |
常见操作
新增渠道:
- "utm_source": ["baidu", "google"]
+ "utm_source": ["baidu", "google", "bytedance"]临时停用某渠道(不删,方便回滚):
- "utm_source": ["baidu", "google", "bytedance"]
+ "utm_source": ["baidu", "google"]线上紧急熔断(怀疑上报有问题,先止血):
- "killSwitch": false
+ "killSwitch": trueCDN 部署建议
- 响应头
Cache-Control: max-age=300, public(5 分钟)。过短会增加 CDN 回源, 过长配置生效慢。出现紧急情况时可以临时改成max-age=30。 - 配置 CORS 允许业务域名 GET。
- JSON 内容紧凑(gzip 后 < 200B),不要塞注释。
- 建议为不同环境(test / prod)部署不同 URL,业务
runtimeConfig切换。
不要在 JSON 里塞 utm 之外的扩展字段
当前 schema 故意精简,未来需要扩展(比如加 utm_medium 维度白名单)时再走 version: 2,避免业务方反复升级 SDK 才能识别新字段。
fail-open 与 killSwitch 的区别
| 场景 | 是否上报 |
|---|---|
| CDN 拉取失败(网络 / 5xx / CORS) | ✅ 上报(fail-open) |
CDN 返回 JSON 但字段非法 / version 不识别 | ✅ 上报(fail-open) |
CDN 返回 killSwitch: true | ❌ 不上报(显式熔断) |
CDN 返回 killSwitch: false 但 utm_source 不命中 | ❌ 不上报(白名单生效) |
URL 没有 utm_source | ❌ 不上报(非投放流量) |
fail-open 保证 CDN 故障不会拖累投放数据采集;killSwitch 是运营/SRE 主动止血的手段。
自定义 fetch(Nuxt 用 $fetch)
fetch 入参用来注入业务侧已有的 HTTP 客户端。这一份 fetch 同时用于 CDN 配置拉取 和 landing 上报:
const trace = new TripoTrace({
baseUrl: 'https://api-cn-test.tripo3d.com',
configUrl: 'https://cdn.example.com/trace/whitelist.json',
fetch: $fetch.raw, // Nuxt ofetch 实例
});这样所有请求都会复用 Nuxt 的 baseURL、timeout、错误重试等配置。注意自定义 fetch 必须满足 Web Fetch API 签名(接收 url + init,返回 Response)。
为什么不强制注入 $fetch
库不感知 Nuxt,保留原生 fetch 兜底,让 React/Vite/普通 SPA 等场景也能直接用。
错误处理
const trace = new TripoTrace({
baseUrl: '...',
configUrl: '...',
onError: (err, ctx) => {
if (ctx.phase === 'init') {
// device id 加载失败:可能是隐身模式 + Canvas/WebGL 被禁
console.warn('[trace] device id 加载失败', err);
} else if (ctx.phase === 'config') {
// CDN 白名单拉取失败 / 解析失败:SDK 会 fail-open 继续上报
console.warn('[trace] CDN 白名单加载失败(已 fail-open)', err);
} else if (ctx.phase === 'report') {
// 上报请求失败:网络问题、CORS、5xx
console.warn('[trace] landing 上报失败', err);
}
},
});库内部不会重试任何失败请求,避免雪崩。失败的 landing 也不会写 dedupe 标记,下次访问同 URL 会重试。
上报成功钩子
const trace = new TripoTrace({
baseUrl: '...',
configUrl: '...',
onReport: (payload) => {
// 用于业务侧的链路监控(比如转发到 Sentry / 数据看板)
myMonitor.track('attribution_landing', payload);
},
});钩子在 dedupe 标记写入之后触发,保证幂等:同一 URL 在 1 分钟窗口内只会触发一次。 payload.landing_url 已经包含完整 query string,业务侧可以自行 parse 出 utm 各字段。
SPA 路由变化
库本身只在 landing() 调用时检查一次 entry URL。但 SDK 用的是构造时缓存的 entry URL,不是当次 window.location.href —— 这意味着 SPA 内部跳转后再调 landing(),判定的仍然是用户首次进入站点的 URL:
// Vue Router
router.afterEach(() => {
trace.landing();
});这次重复调用的意义在于:
| 场景 | 行为 |
|---|---|
| 首次 init 时 device id 还没解析完成 | 第二次调用补上 landing 上报 |
| 首次调用时 CDN 拉取失败 | 不会自动重试(同实例 CDN 只拉一次) |
URL 已通过 history.replaceState 改写 | 不影响判定(entry URL 已经缓存) |
| 已经上报过 | dedupe 命中,不发请求 |
跨页归因
如果业务方希望「用户点站内链接,进入第二个带 utm 的 URL 时也归因」,这种场景 SDK 不直接支持(一次实例只缓存一次 entry URL)。推荐做法是在第二个页面 reload 或重新 new TripoTrace 一次。
自管 device id(业务自有 ID 体系)
如果业务已经有自己的 visitor id(例如登录用户的 user_id 哈希),可以跳过指纹计算:
const userIdHash = await fetchUserIdFromBackend();
const trace = new TripoTrace({
baseUrl: '...',
configUrl: '...',
deviceIdOverride: userIdHash,
});
await trace.init(); // 直接用 userIdHash 当 device id
trace.landing();deviceIdOverride 也会被写入 localStorage,下次访问继续生效。
注意混用风险
deviceIdOverride 一旦写入 localStorage,再次访问时(即便不传 override)也会用缓存值。如果业务的 user id 体系会变(登出/换号),需要业务侧主动清理 localStorage['tripo:device_id']。
直接调用底层工具
库重导出了 fingerprint 的 hash 函数,便于业务做相关 key 计算:
import { murmurX64Hash128 } from '@tripo3d/trace';
const stableKey = murmurX64Hash128(`my-business-${userId}-${context}`);完整导出清单
// 主类
export { TripoTrace }
// 类型
export type {
LandingPayload,
RemoteTraceConfig,
TraceErrorContext,
TripoTraceParams,
}
// 底层工具
export { murmurX64Hash128 } // re-exported from @tripo3d/fingerprint完整签名见 API Reference。