yl-tracker-sdk
v1.1.2
Published
Multi-tenant user behavior tracking SDK for Nuxt/Vue/React
Readme
yl-tracker-sdk
多租户用户行为埋点 SDK — Nuxt 3 (SSR) / Vue 3 / React 18+ / Vanilla TS,开箱即用
A multi-tenant front-end analytics SDK with first-touch UTM attribution, auto page-view & click tracking, batched send with retry, and SSR-safe storage.
✨ 特性 / Features
| 能力 | 说明 |
|------|------|
| 🏢 多租户隔离 | 所有存储键带 ana_{appId}_ 前缀,同一域名可安全接入多个应用 |
| 🧭 自动页面浏览 | 监听 SPA 路由变化,自动上报 $page_view |
| 🖱️ 自动点击采集 | 事件委托,捕获 tag/text/href/xpath,无侵入 |
| ⏱️ 页面停留时长 | 记录进入/离开时间,自动附带 page_stay_time |
| 🎯 UTM 首触点归因 | 仅在首次访问时持久化,后续跨会话保留 |
| 🔄 会话管理 | 30 分钟无活动自动开新会话(可配) |
| 📦 批量发送 | 默认 10 条/批、5s 间隔,支持 Beacon 降级和指数退避重试 |
| 🌐 SSR 友好 | Nuxt 3 提供 createNuxtServerTracker,自动从请求头提取 IP/UA/Cookie |
| 🍪 可插拔存储 | 内置 localStorage / Memory / Cookie 三种,可自定义 |
| 🪶 轻量 | 主入口 gzip 后约 4.8 kB,零运行时依赖 |
| 📜 完整类型 | 全量 .d.ts 导出,IDE 智能提示开箱即用 |
📦 安装 / Installation
# pnpm(推荐)
pnpm add yl-tracker-sdk
# npm
npm install yl-tracker-sdk
# yarn
yarn add yl-tracker-sdk按框架导入对应子包,构建工具会自动 tree-shake:
import { Tracker } from 'yl-tracker-sdk' // 核心类(任意环境)
import { ... } from 'yl-tracker-sdk/nuxt' // Nuxt 3 专用
import { ... } from 'yl-tracker-sdk/vue' // Vue 3 专用
import { ... } from 'yl-tracker-sdk/react' // React 18+ 专用🚀 快速开始 / Quick Start
Nuxt 3 (推荐,支持 SSR)
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
tracker: {
appId: 'my_app',
apiUrl: 'https://api.example.com/api/track',
},
},
},
})// plugins/tracker.client.ts
import { createNuxtTracker } from 'yl-tracker-sdk/nuxt'
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const tracker = createNuxtTracker(config.public.tracker)
nuxtApp.provide('tracker', tracker)
})<!-- 任意 .vue 文件 -->
<script setup lang="ts">
const { $tracker } = useNuxtApp()
$tracker.track('login_success', { method: 'email' })
</script>SSR 场景需在服务端补一个
plugins/tracker.server.ts,完整示例见下方 "Nuxt 3 SSR 集成" 章节。
Nuxt 3 SSR 集成
SSR 下需要在服务端用一个单独的 tracker 实例(读不到 localStorage,要用 Cookie 共享 ID),并在响应回写 Set-Cookie:
// plugins/tracker.server.ts
import { createNuxtServerTracker } from 'yl-tracker-sdk/nuxt'
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const event = useRequestEvent()
const { tracker, cookieAdapter } = createNuxtServerTracker(
config.public.tracker,
event,
)
// 把 ID 写回 Cookie,让客户端能接力
const headers = cookieAdapter.getSetCookieHeaders()
if (event?.node?.res && headers.length) {
event.node.res.setHeader('Set-Cookie', headers)
}
// 异步事件别忘了在请求结束时 flush
event?.node?.res?.on?.('close', () => {
tracker.flush().catch(() => {})
})
return {
provide: { tracker },
}
})// plugins/tracker.client.ts
import { createNuxtTracker } from 'yl-tracker-sdk/nuxt'
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const tracker = createNuxtTracker(config.public.tracker)
return {
provide: { tracker },
}
})Vue 3
// main.ts
import { createApp } from 'vue'
import { TrackerPlugin } from 'yl-tracker-sdk/vue'
import App from './App.vue'
const app = createApp(App)
app.use(TrackerPlugin, {
appId: 'my_app',
apiUrl: 'https://api.example.com/api/track',
})
app.mount('#app')<script setup lang="ts">
import { useTracker } from 'yl-tracker-sdk/vue'
const { track, setUserId } = useTracker()
function handleLogin(userId: string) {
setUserId(userId)
track('login_success')
}
</script>React 18+
// main.tsx
import { TrackerProvider } from 'yl-tracker-sdk/react'
function App() {
return (
<TrackerProvider
appId="my_app"
apiUrl="https://api.example.com/api/track"
>
<Router />
</TrackerProvider>
)
}// 任意组件
import { useTracker } from 'yl-tracker-sdk/react'
function LoginButton() {
const { track, setUserId } = useTracker()
const handleLogin = (userId: string) => {
setUserId(userId)
track('login_success', { method: 'google' })
}
return <button onClick={() => handleLogin('user_123')}>Login</button>
}Vanilla / 其他框架
import { Tracker, BrowserStorage } from 'yl-tracker-sdk'
const tracker = new Tracker(
{
appId: 'my_app',
apiUrl: 'https://api.example.com/api/track',
},
new BrowserStorage(),
)
tracker.track('button_click', { btn: 'submit' })📖 核心 API
tracker.track(eventName, properties?)
手动追踪自定义事件。事件会进入发送队列,按 batchSize / flushInterval 批量上报。
tracker.track('purchase', { price: 99, currency: 'CNY', sku: 'A001' })tracker.trackPageView(properties?)
手动触发一次页面浏览事件。默认情况下 SDK 会在 SPA 路由切换时自动调用。
tracker.setUserId(userId) / tracker.clearUserId()
登录/登出时调用,会持久化到存储并附带在之后所有事件上。
tracker.setUserId('user_123')
// ...
tracker.clearUserId() // 登出tracker.setUserProperties(props)
设置用户属性(如 VIP 等级、用户名等),合并到之后所有事件的 properties 中,自动持久化。
tracker.setUserProperties({ vip_level: 'gold', register_date: '2024-01-01' })tracker.flush()
立即刷新发送队列,返回 Promise。SSR 环境下需要在请求结束时调用,否则事件可能丢失。
await tracker.flush()tracker.destroy()
停止定时器、解绑事件监听,会先尝试 flush 剩余事件。SPA 卸载或测试时调用。
tracker.getAnonymousId() / tracker.getSessionId()
获取当前访客的匿名 ID(首次访问时生成,永久保留)和会话 ID(30 分钟无活动后重置)。
⚙️ 配置项 / Configuration
interface TrackerConfig {
/** 必填 · 应用 ID(多租户隔离的 key) */
appId: string
/** 必填 · 上报接口地址,如 https://api.example.com/api/track */
apiUrl: string
/** 可选 · API Key(后端鉴权,优先级高于 appId) */
apiKey?: string
/** 批量发送条数,默认 10 */
batchSize?: number
/** 批量发送间隔(毫秒),默认 5000 */
flushInterval?: number
/** 调试模式:打印事件 payload,默认 false */
debug?: boolean
/** 自动采集页面浏览,默认 true */
autoTrackPageView?: boolean
/** 自动采集点击事件,默认 true */
autoTrackClick?: boolean
/** 会话超时(毫秒),默认 1800000(30 分钟) */
sessionTimeout?: number
/** 自定义追踪参数名列表,首触点归因,自动持久化并附加到所有事件 */
trackingParams?: string[]
}🧠 自动采集规则
| 事件 | 触发时机 | 附带属性 |
|------|---------|---------|
| $page_view | 初始化 + SPA 路由切换 | $url, $url_path, $title, $referrer |
| $click | 全局事件委托捕获点击 | tag_name, text, href, class_name, id, xpath |
| page_stay_time | 下一次 page_view 触发时附带 | 上一个页面的停留毫秒数 |
如需禁用,把对应 autoTrackXxx 设为 false 即可。
🗃️ 自定义存储
默认使用 localStorage。如需 Cookie(SSR 共享)或纯内存(隐私模式):
import { Tracker, CookieStorage, MemoryStorage } from 'yl-tracker-sdk'
// Cookie 存储(SSR 场景常用)
new Tracker(config, new CookieStorage(cookieAdapter))
// 内存存储(浏览器关闭即丢)
new Tracker(config, new MemoryStorage())
// 写你自己的存储:实现 { get, set, remove } 三个方法即可
class MyStorage {
get(key: string) { /* ... */ }
set(key: string, value: string) { /* ... */ }
remove(key: string) { /* ... */ }
}
new Tracker(config, new MyStorage())🛡️ 数据格式
上报到 apiUrl 的请求体为 JSON,结构如下:
interface TrackEvent {
app_id: string // 租户 ID
event_name: string // 事件名
user_id?: string // 登录后才有
anonymous_id: string // 匿名 ID(永久)
session_id: string // 会话 ID
timestamp: number // 毫秒时间戳
properties?: Record<string, any> // 自定义属性(含用户属性)
context?: { // 环境信息
url?: string
url_path?: string
title?: string
referrer?: string
user_agent?: string
screen?: string
platform?: string
viewport_width?: string
viewport_height?: string
screen_width?: string
screen_height?: string
timezone_offset?: string
language?: string
ip?: string // SSR 注入
}
utm?: { // 首触点归因
utm_source?: string
utm_medium?: string
utm_campaign?: string
utm_term?: string
utm_content?: string
}
event_duration?: number // 事件耗时(可选)
page_stay_time?: number // 页面停留时长(可选)
}🌐 浏览器兼容性
- Chrome / Edge / Firefox / Safari 最近 2 个大版本
- 移动端 iOS Safari 14+、Android Chrome 90+
- SSR:Node.js 18+ (Nuxt 3 / Nitro)
🛠️ 开发 / Development
# 装依赖
pnpm install
# 开发(监听文件变化并构建)
pnpm dev
# 完整构建(产物在 dist/)
pnpm build
# 跑测试
pnpm test📚 文档 / Documentation
本 README 已覆盖所有公共 API、配置项、框架集成方式,适合作为接入起点。
🔒 团队内部详细文档(业务接入指南、管理后台、部署手册等)在源码仓库的
docs/目录,通过内网 GitLab 访问,不公开发布。
🤝 贡献 / Contributing
PR / Issue 欢迎!请确保:
- 通过
pnpm test - 保持 100% TypeScript 类型覆盖
- 公共 API 变更请先开 issue 讨论
📄 License
MIT © 2024-present
