npm package: @mobikechip-wanji-gui/tracker-sdk
Description: Mobikechip 用户行为追踪 SDK - 兼容 Vue 2 和 Vue 3
Latest version: 1.0.10

# Mobikechip Tracker SDK

[![Version](https://img.shields.io/badge/version-1.0.4-blue.svg)](https://github.com/mobikechip/tracker-sdk)
[![Vue 2](https://img.shields.io/badge/Vue-2.6+-green.svg)](https://vuejs.org/)
[![Vue 3](https://img.shields.io/badge/Vue-3.x-green.svg)](https://vuejs.org/)
[![License](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)

Mobikechip 用户行为追踪 SDK - 支持 Vue 2 和 Vue 3

## 特性

- ✅ **双版本兼容**：同时支持 Vue 2.6+ 和 Vue 3.x
- ✅ **自动ID管理**：自动生成和管理 anonymous_id 和 session_id
- ✅ **智能用户ID获取**：支持多种方式获取登录用户ID
- ✅ **自动用户关联**：检测用户登录状态变化，自动关联匿名ID与用户ID
- ✅ **设备信息收集**：自动收集浏览器、操作系统、屏幕等设备信息
- ✅ **简单易用**：通过 Vue.use() 安装，通过 this.$track() 调用
- ✅ **静默失败**：网络错误不影响应用正常运行
- ✅ **完整测试**：单元测试和集成测试覆盖
- ✅ **支持调试**：可选的调试模式，便于开发和排查问题

## 安装

```bash
npm install @mobikechip-wanji/tracker-sdk
```

### 运行时依赖

- SDK 在浏览器环境中基于原生 `fetch` 实现 HTTP 通信，无需额外安装任何网络库。

## 快速开始

### Vue 2

```javascript
// main.js
import Vue from 'vue'
import TrackerSDK from '@mobikechip-wanji/tracker-sdk'

Vue.use(TrackerSDK, {
  projectName: 'your-project-name',
  apiEndpoint: '/api/track',
  debug: false
})
```

### Vue 3

```javascript
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import TrackerSDK from '@mobikechip-wanji/tracker-sdk'

const app = createApp(App)

app.use(TrackerSDK, {
  projectName: 'your-project-name',
  apiEndpoint: '/api/track',
  debug: false
})

app.mount('#app')
```

## 配置选项

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| projectName | string | ✅ | - | 项目名称，用于标识事件来源 |
| apiEndpoint | string | ❌ | `/api/track` | 事件上报的 API 端点 |
| debug | boolean | ❌ | `false` | 是否启用调试模式 |
| getUserId | function | ❌ | `null` | 自定义获取用户ID的函数 |

### 示例配置

```javascript
const config = {
  projectName: 'mobikechip-mall',
  apiEndpoint: 'https://api.mobikechip.com/v1/track',
  debug: process.env.NODE_ENV === 'development',
  getUserId: () => {
    // 自定义获取用户ID的逻辑
    return window.appState?.user?.id || null
  }
}
```

### 设备属性说明（deviceProperties）

- userAgent：浏览器 UA 字符串
- browser：浏览器名称（Chrome/Firefox/Safari/Edge/Opera/IE/Unknown）
- browserVersion：浏览器主版本号
- os：操作系统（如 Windows 10、macOS、Android、iOS 等）
- osVersion：操作系统主版本号（新增）
- screenWidth/screenHeight：屏幕宽高（像素）
- screenResolution：屏幕分辨率字符串，如 1920x1080
- language：语言，如 zh-CN
- timezone：时区，如 Asia/Shanghai

## 使用方法

### 在组件中调用 $track 方法

#### Vue 2 / Vue 3 (Options API)

```javascript
export default {
  methods: {
    handleClick() {
      this.$track('button_click', {
        button_id: 'search-btn',
        page: '/products'
      })
    },

    async handleAsyncAction() {
      const result = await this.$track('async_event', {
        action: 'data_fetch',
        duration: 1000
      })

      if (result.success) {
        console.log('Event tracked successfully')
      }
    },
    
    handleSyncAction() {
      // 使用同步版本，不等待结果
      this.$trackSync('sync_event', {
        action: 'quick_track'
      })
      // 事件会在后台异步发送
    }
  }
}
```

#### Vue 3 (Composition API)

```javascript
import { getCurrentInstance } from 'vue'

export default {
  setup() {
    const instance = getCurrentInstance()
    const track = instance?.appContext.config.globalProperties.$track

    const handleClick = () => {
      track('button_click', {
        button_id: 'search-btn'
      })
    }

    return { handleClick }
  }
}
```

### 追踪不同类型的事件

#### 页面浏览事件

```javascript
// 在路由守卫中
router.beforeEach((to, from, next) => {
  app.$track('page_view', {
    page: to.path,
    page_title: to.meta.title,
    referrer: from.path
  })
  next()
})
```

#### 用户操作事件

```javascript
export default {
  methods: {
    handleSearch() {
      this.$track('search', {
        search_keyword: this.keyword,
        result_count: this.results.length
      })
    },

    handlePurchase(productId) {
      this.$track('purchase', {
        product_id: productId,
        price: this.product.price,
        quantity: this.quantity
      })
    }
  }
}
```

#### 自定义事件

```javascript
export default {
  mounted() {
    // 追踪组件加载时间
    const loadTime = Date.now() - window.performance.timing.navigationStart

    this.$track('component_load', {
      component_name: 'ProductList',
      load_time: loadTime
    })
  },

  methods: {
    handleError(error) {
      this.$track('error', {
        error_message: error.message,
        error_stack: error.stack,
        page: this.$route.path
      })
    }
  }
}
```

## 事件数据结构

SDK 会自动组装以下事件数据：

```javascript
{
  projectName: 'your-project-name',    // 从配置获取
  eventName: 'button_click',            // 从 $track 参数获取
  userId: 'user123',                    // 当前登录用户ID（可为空）
  anonymousId: 'anon-uuid-xxx',         // SDK自动生成（存储在 localStorage）
  sessionId: 'session-uuid-xxx',        // SDK自动生成（存储在 sessionStorage）
  eventTime: '2025-10-28T10:30:00.123Z', // 事件发生时间（ISO 8601格式）
  eventProperties: {                    // 从 $track 参数获取
    button_id: 'search-btn',
    page: '/products'
  },
  deviceProperties: {                   // SDK自动收集
    browser: 'Chrome',
    browserVersion: '118',
    os: 'Windows 10',
    osVersion: '10',                   // 新增：操作系统主版本号
    screenResolution: '1920x1080',
    language: 'zh-CN',
    timezone: 'Asia/Shanghai'
  }
}
```

后端接收的数据格式为数组：

```javascript
[
  {
    // 事件对象
  }
]
```

## ID 管理

### Anonymous ID

- **作用**：识别匿名用户，追踪用户在多个会话中的行为
- **格式**：UUID v4
- **存储**：`localStorage` (key: `mbk_tracker_anonymous_id`)
- **生命周期**：永久（除非用户清除浏览器数据）

### Session ID

- **作用**：识别单次访问会话，追踪单次会话中的用户行为
- **格式**：UUID v4
- **存储**：`sessionStorage` (key: `mbk_tracker_session_id`)
- **生命周期**：浏览器标签页关闭后销毁，或30分钟无活动后过期

### User ID

- **作用**：识别登录用户
- **获取方式**：SDK 支持多种获取方式（优先级从高到低）：
  1. 自定义 `getUserId` 函数
  2. 全局变量 `window.currentUser.id`
  3. Vue 全局属性 `$user.id`
  4. Vuex store `state.user.id`
  5. Pinia store `user.id`
  6. localStorage 中的用户信息
- **默认值**：`null`

## 用户ID获取和关联

### 自动用户ID获取

SDK 支持多种方式自动获取用户ID，按优先级从高到低：

#### 1. 自定义获取函数（推荐）

```javascript
Vue.use(TrackerSDK, {
  projectName: 'my-app',
  getUserId: () => {
    // 从应用状态管理中获取用户ID
    return store.state.user?.id || null
  }
})
```

#### 2. 全局变量

```javascript
// 设置全局用户对象
window.currentUser = { id: 'user_123', name: 'John' }

// SDK 会自动获取
const userId = getUserId() // 'user_123'
```

#### 3. Vue 全局属性

```javascript
// Vue 3
app.config.globalProperties.$user = { id: 'user_456' }

// Vue 2
Vue.prototype.$user = { id: 'user_456' }
```

#### 4. Vuex/Pinia Store

```javascript
// Vuex
window.$store = {
  state: { user: { id: 'user_789' } }
}

// Pinia
window.$pinia._s.get('user').id = 'user_789'
```

#### 5. localStorage

```javascript
localStorage.setItem('user_info', JSON.stringify({
  id: 'user_001',
  name: 'Alice'
}))
```

### 自动用户关联

当检测到用户登录状态变化（从 `null` 到有值）时，SDK会自动发送 `user_association` 事件：

```javascript
// 用户未登录时
tracker.track('page_view') // userId: null

// 用户登录后
tracker.track('login_success') // 自动发送关联事件 + userId: 'user_123'
```

关联事件数据结构：
```javascript
{
  eventName: 'user_association',
  userId: 'user_123',
  anonymousId: 'anon-uuid-xxx',
  eventTime: '2025-11-06T09:14:00.000Z',
  eventProperties: {
    associationType: 'login',
    previousUserId: null
  },
  deviceProperties: { /* 设备信息 */ }
}
```

### 手动用户关联

对于特殊场景，可以手动触发用户关联：

```javascript
// 在组件中
export default {
  methods: {
    async handleLogin() {
      // 登录逻辑...
      const userId = 'user_456'
      
      // 手动关联
      const result = await this.$associateUser(userId)
      
      if (result.success) {
        console.log('用户关联成功')
      }
    }
  }
}
```

### 编程式使用

```javascript
import { createTracker } from '@mobikechip-wanji/tracker-sdk'

const tracker = createTracker({
  projectName: 'my-app',
  getUserId: () => window.appState?.user?.id
})

// 追踪事件
await tracker.track('custom_event', { property: 'value' })

// 同步追踪（不等待结果）
tracker.trackSync('quick_event', { property: 'value' })

// 手动关联
await tracker.associateUser('user_789')

// 获取配置
const config = tracker.getConfig()

// 调试模式下更新配置
tracker.updateConfig({ debug: true })
```

## 调试模式

启用调试模式后，SDK会输出详细的日志信息：

```javascript
Vue.use(TrackerSDK, {
  projectName: 'your-project',
  debug: true  // 开发环境开启
})
```

调试模式下，SDK会输出：
- 插件安装信息
- 用户ID获取详情
- 用户关联检测
- 事件追踪详情
- 网络请求状态
- 错误信息

### 调试模式下的配置更新

在调试模式下，可以动态更新配置：

```javascript
// 在组件中
export default {
  mounted() {
    // 获取当前配置
    const config = this.$track.getConfig()
    
    // 更新配置（仅在调试模式下有效）
    this.$track.updateConfig({
      apiEndpoint: '/new-api-endpoint',
      debug: true
    })
  }
}
```

## 错误处理

SDK采用静默失败策略，确保网络错误不影响应用正常运行：

```javascript
const result = await this.$track('event_name', data)

// 检查追踪结果
if (!result.success) {
  console.warn('Event tracking failed:', result.error)
  // 可选：上报错误监控系统
}
```

## 最佳实践

### 1. 事件命名规范

- 使用下划线命名：`page_view`, `button_click`
- 保持语义清晰：`user_login` 而非 `login_user`
- 避免过于细粒度：合并相似的点击事件

### 2. 属性命名规范

- 使用下划线或camelCase
- 包含上下文信息：`user_id`, `product_name`
- 避免敏感信息：不要发送密码、Token等

### 3. 性能建议

- 避免高频事件：在短时间内大量调用 `$track`
- 合理使用异步：使用 `await` 等待结果，或使用 `trackSync` 进行非阻塞调用
- 批量上报：未来版本将支持批量事件上报
- 事件去重：避免重复追踪相同的事件
- 延迟加载：对于非关键事件，可以考虑延迟上报

### 4. 错误处理建议

- 始终检查追踪结果：`await this.$track()` 返回 `{ success, error }`
- 实现重试机制：对于重要事件，可以在失败时重试
- 降级策略：当追踪服务不可用时，确保应用正常运行
- 错误监控：将追踪错误上报到错误监控系统

```javascript
// 示例：带重试的事件追踪
async function trackWithRetry(eventName, properties, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const result = await this.$track(eventName, properties)
    if (result.success) return result
    
    // 指数退避
    await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000))
  }
  
  // 最终失败，记录到错误监控系统
  console.error(`Failed to track event after ${maxRetries} retries:`, eventName)
  return { success: false, error: 'Max retries exceeded' }
}
```

## 常见问题

### Q: 如何在 Composition API 中使用？
A: 使用 `getCurrentInstance` 获取全局属性：

```javascript
import { getCurrentInstance } from 'vue'

export default {
  setup() {
    const { appContext } = getCurrentInstance()
    const track = appContext.config.globalProperties.$track

    return { track }
  }
}
```

### Q: 如何自定义 API 端点？
A: 在配置中指定 `apiEndpoint`：

```javascript
app.use(TrackerSDK, {
  projectName: 'your-project',
  apiEndpoint: 'https://custom-api.com/track'
})
```

### Q: 如何处理网络错误？
A: SDK采用静默失败策略，错误不会中断应用。可以通过返回值判断是否成功：

```javascript
const result = await this.$track('event')
if (!result.success) {
  // 处理错误，如重试或记录日志
}
```

### Q: 如何清除追踪ID？
A: 手动清除存储：

```javascript
// 方法1：手动清除存储
localStorage.removeItem('mbk_tracker_anonymous_id')
sessionStorage.removeItem('mbk_tracker_session_id')

// 方法2：使用提供的工具函数
import { clearAllIds } from '@mobikechip-wanji/tracker-sdk'
clearAllIds()
```

### Q: 如何自定义用户ID获取逻辑？
A: 在配置中提供 `getUserId` 函数：

```javascript
Vue.use(TrackerSDK, {
  projectName: 'my-app',
  getUserId: () => {
    // 自定义逻辑
    return yourCustomUserManager.getCurrentUserId()
  }
})
```

### Q: 用户关联什么时候触发？
A: 当SDK检测到用户ID从 `null` 变为有值时自动触发，也可以手动调用：

```javascript
// 自动触发（登录状态变化）
// 手动触发
await this.$associateUser('user_id')
```

### Q: 如何处理用户登出？
A: 用户登出时，SDK会继续使用相同的匿名ID追踪：

```javascript
// 用户登出后，userId 变为 null，但 anonymousId 保持不变
tracker.track('user_logout') // userId: null, anonymousId: 'same-as-before'
```

## 示例应用

项目提供了两个示例应用：

- [Vue 2 示例](./examples/demo-vue2/)
- [Vue 3 示例](./examples/demo-vue3/)

运行示例：

```bash
# Vue 2 示例
cd examples/demo-vue2
npm install
npm run serve

# Vue 3 示例
cd examples/demo-vue3
npm install
npm run dev

# 用户ID关联示例
node examples/user-id-association.js
```

## 开发指南

### 运行测试

```bash
# 运行所有测试
npm test

# 监听模式
npm run test:watch

# Vue 2 测试
npm run test:vue2

# Vue 3 测试
npm run test:vue3
```

### 构建

```bash
npm run build
```

构建产物：
- `dist/tracker-sdk.umd.js` - UMD 格式（浏览器环境）
- `dist/tracker-sdk.umd.cjs` - UMD 格式（CommonJS require）
- `dist/tracker-sdk.esm.js` - ESM 格式（模块化）

### 发布

#### 📋 发布前检查清单

在发布新版本之前，请确认以下事项：

- [ ] **代码质量**：所有测试通过 (`npm test`)
- [ ] **构建成功**：`npm run build` 无错误
- [ ] **文档更新**：README.md 中的版本号和示例是最新的
- [ ] **示例测试**：示例项目运行正常
- [ ] **Git 状态**：代码已提交到远程仓库
- [ ] **版本规划**：确定是 patch/minor/major 版本更新

#### 🚀 详细发布步骤

##### 1. 环境准备

```bash
# 检查 npm 登录状态
npm whoami

# 如果未登录，执行登录
npm login
# 输入用户名、密码和邮箱
```

##### 2. 代码准备

```bash
# 确保代码已提交
git add .
git commit -m "准备发布 v1.x.x"
git push origin main
```

##### 3. 测试和构建

```bash
# 运行所有测试
npm test

# 构建生产版本
npm run build

# 验证构建产物
ls -la dist/
```

##### 4. 版本管理

```bash
# 补丁版本（修复 bug）：1.0.0 -> 1.0.1
npm version patch

# 次要版本（新功能）：1.0.0 -> 1.1.0
npm version minor

# 主要版本（破坏性变更）：1.0.0 -> 2.0.0
npm version major
```

##### 5. 发布到 npm

```bash
# 发布为公共包（推荐）
npm publish --access public

# 或者修改 package.json 后直接发布
npm publish
```

#### 🛠️ 包名配置说明

##### 作用域包（推荐）
```json
{
  "name": "@mobikechip-wanji/tracker-sdk",
  "publishConfig": {
    "access": "public"
  }
}
```

##### 非作用域包
```json
{
  "name": "mobikechip-tracker-sdk"
}
```

#### ⚠️ 常见发布问题及解决方案

##### 问题 1：认证失败
```bash
npm error code ENEEDAUTH
npm error need auth This command requires you to be logged in
```
**解决方案**：
```bash
npm login
```

##### 问题 2：私有包权限错误
```bash
npm error code E402
npm error 402 Payment Required - You must sign up for private packages
```
**解决方案**：
```bash
# 方法1：发布为公共包
npm publish --access public

# 方法2：修改 package.json
{
  "publishConfig": {
    "access": "public"
  }
}
```

##### 问题 3：作用域不存在
```bash
npm error code E404
npm error 404 Not Found - Scope not found
```
**解决方案**：
1. **创建组织**：访问 https://www.npmjs.com/org/create 创建 `@mobikechip-wanji-wanji` 组织
2. **使用个人作用域**：改为 `@你的用户名/tracker-sdk`
3. **使用非作用域包名**：改为 `mobikechip-tracker-sdk`

##### 问题 4：版本冲突
```bash
npm error code E403
npm error 403 Forbidden - You cannot publish over the existing version
```
**解决方案**：
```bash
# 更新版本号
npm version patch
npm publish
```

#### ✅ 发布后验证

##### 1. 验证包是否发布成功
```bash
# 查看包信息
npm view @mobikechip-wanji/tracker-sdk

# 查看包版本
npm info @mobikechip-wanji/tracker-sdk versions

# 查看包详情
npm info @mobikechip-wanji/tracker-sdk
```

##### 2. 测试安装
```bash
# 在新目录中测试安装
mkdir test-sdk && cd test-sdk
npm init -y
npm install @mobikechip-wanji/tracker-sdk
```

##### 3. 更新文档
- [ ] 更新 GitHub Releases
- [ ] 通知相关团队
- [ ] 更新项目文档中的版本号

#### 📈 版本管理最佳实践

##### 语义化版本控制
- **主版本号**：不兼容的 API 修改
- **次版本号**：向下兼容的功能性新增
- **修订号**：向下兼容的问题修正

##### 发布流程建议
1. **开发分支**：在 `develop` 分支开发新功能
2. **测试验证**：完整的测试覆盖
3. **版本标记**：使用 Git 标签标记版本
4. **发布记录**：维护详细的 CHANGELOG

##### 自动化发布（可选）
```json
{
  "scripts": {
    "release": "npm run test && npm run build && npm version patch && npm publish --access public",
    "release:minor": "npm run test && npm run build && npm version minor && npm publish --access public",
    "release:major": "npm run test && npm run build && npm version major && npm publish --access public"
  }
}
```

## 技术栈

- **Vue**：2.6+ / 3.x
- **JavaScript**：ES6+
- **测试**：Jest 27+
- **构建**：Rollup 2.x
- **HTTP客户端**：浏览器原生 fetch（无额外依赖）

## 许可证

MIT License

## 更新日志

### v1.0.4

- 🐛 修复：构建产物文件名不一致问题
- ✅ 改进：完善文档和示例代码

### v1.0.3

- ✨ **新增**：trackSync 方法，支持非阻塞式事件追踪
- ✨ **新增**：clearAllIds 工具函数，便于清理追踪ID
- ✅ 改进：HTTP客户端基于浏览器原生fetch实现
- ✅ 改进：增强错误处理和调试日志

### v1.0.2

- ✨ **新增**：智能用户ID获取功能
- ✨ **新增**：自动用户关联功能
- ✨ **新增**：手动用户关联API
- ✨ **新增**：支持多种用户状态管理方式
- ✨ **新增**：用户关联事件自动发送
- ✅ 改进：调试模式增加用户ID获取日志
- ✅ 改进：配置验证支持自定义函数检查

### v1.0.0

- ✨ 初始版本
- ✅ 支持 Vue 2.6+ 和 Vue 3.x
- ✅ 自动 ID 管理（anonymous_id 和 session_id）
- ✅ 设备信息收集
- ✅ HTTP 事件上报
- ✅ 完整的单元测试和集成测试
- ✅ 调试模式支持

## 支持

如有问题或建议，请提交 [Issue](https://github.com/mobikechip/tracker-sdk/issues)。

---

**Mobikechip Tracker SDK** - 让用户行为追踪变得简单