Skip to content

高级用法

自定义渠道

v0.1 内置 baidubd_vid)一个渠道。库内部用一个简单的检测器协议:

ts
import type { ChannelDetector } from '@tripo3d/trace';

interface ChannelDetector {
  name: ChannelName;
  detect: (url: URL) => string | null;
}

只要从 URL 中能拿到一个非空字符串,就视为命中(值会作为 click_id 用于业务分析)。

未来要加 google gclid 时只需在库里加:

ts
// channels/google.ts
export const googleChannel: ChannelDetector = {
  name: 'google',
  detect: url => url.searchParams.get('gclid'),
};

然后注册进 channels/index.tsALL_CHANNELS 列表即可。

自定义 fetch(Nuxt 用 $fetch)

fetch 入参用来注入业务侧已有的 HTTP 客户端:

ts
const trace = new TripoTrace({
  baseUrl: 'https://api-cn-test.tripo3d.com',
  fetch: $fetch.raw, // Nuxt ofetch 实例
});

这样上报请求会复用 Nuxt 的 baseURL、timeout、错误重试等配置。注意自定义 fetch 必须满足 Web Fetch API 签名(接收 url + init,返回 Response)。

为什么不强制注入 $fetch

库不感知 Nuxt,保留原生 fetch 兜底,让 React/Vite/普通 SPA 等场景也能直接用。

错误处理

ts
const trace = new TripoTrace({
  baseUrl: '...',
  onError: (err, ctx) => {
    if (ctx.phase === 'init') {
      // device id 加载失败:可能是隐身模式 + Canvas/WebGL 被禁
      console.warn('[trace] device id 加载失败', err);
    } else if (ctx.phase === 'report') {
      // 上报请求失败:网络问题、CORS、5xx
      console.warn('[trace] landing 上报失败', ctx.channel, err);
    }
  },
});

库内部不会重试任何失败请求,避免雪崩。失败的 landing 也不会写 dedupe 标记,下次访问同 URL 会重试。

上报成功钩子

ts
const trace = new TripoTrace({
  baseUrl: '...',
  onReport: (payload, channel) => {
    // 用于业务侧的链路监控(比如转发到 Sentry / 数据看板)
    myMonitor.track('attribution_landing', { channel, ...payload });
  },
});

钩子在 dedupe 标记写入之后触发,保证幂等:同一日同一 URL 只会触发一次。

SPA 路由变化

库本身只在 initLanding() 调用时检查一次。SPA 用户从 /?bd_vid=A 跳到 /?bd_vid=B,业务侧需要主动再调一次:

ts
// Vue Router
router.afterEach(() => {
  void trace.initLanding();
});

initLanding() 是幂等的:

场景行为
第二次调用,URL 没变dedupe 命中 → 不发请求
URL 变了但无渠道matchChannel 返回 undefined → 不发
URL 变了且命中新 click_id 当日未报正常上报

device id 已缓存在内存与 localStorage,第二次起几乎是零成本。

自管 device id(业务自有 ID 体系)

如果业务已经有自己的 visitor id(例如登录用户的 user_id 哈希),可以跳过指纹计算:

ts
const userIdHash = await fetchUserIdFromBackend();

const trace = new TripoTrace({
  baseUrl: '...',
  deviceIdOverride: userIdHash,
});

await trace.initLanding(); // 直接用 userIdHash 当 device id

deviceIdOverride 也会被写入 localStorage,下次访问继续生效。

注意混用风险

deviceIdOverride 一旦写入 localStorage,再次访问时(即便不传 override)也会用缓存值。如果业务的 user id 体系会变(登出/换号),需要业务侧主动清理 localStorage['tripo:device_id']

直接调用底层工具

库重导出了 fingerprint 的 hash 函数,便于业务做相关 key 计算:

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

const stableKey = murmurX64Hash128(`my-business-${userId}-${context}`);

也直接导出 matchChannelbaiduChannel

ts
import { baiduChannel, matchChannel } from '@tripo3d/trace';

const url = new URL(window.location.href);
console.log(baiduChannel.detect(url));         // 'bd_vid 的值' 或 null
console.log(matchChannel(url));                // 命中的 ChannelDetector

完整导出清单

ts
// 主类
export { TripoTrace }

// 类型
export type {
  ChannelName,
  LandingPayload,
  TraceErrorContext,
  TripoTraceParams,
  ChannelDetector,
}

// 渠道扩展
export { baiduChannel, matchChannel }

// 底层工具
export { murmurX64Hash128 } // re-exported from @tripo3d/fingerprint

完整签名见 API Reference

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