@sensorswave/uniapp-sdk
v1.3.0
Published
Sensors Wave uni-app Analytics SDK
Readme
Sensors Wave UniApp SDK
Sensors Wave UniApp 数据采集 SDK 是一个跨平台的数据埋点采集库,一套代码即可在多个平台自动采集用户行为数据。 如果你是第一次接触 Sensors Wave,欢迎访问 sensorswave.com 了解产品并创建账号。
特性
- 🌐 跨平台支持:微信 / 支付宝 / 抖音 / 百度小程序、App(iOS / Android / HarmonyOS)、H5
- 🚀 自动采集:页面浏览与离开、应用启动与退出、元素点击、小程序分享等全生命周期埋点
- 🧩 Vue 插件:通过
app.use()一键接入,自动注入页面生命周期 mixin,零侵入采集页面事件 - 👤 用户体系:匿名 / 登录用户标识、用户属性(profile)增删改、公共属性注册
- 🧪 A/B 测试:功能开关(Feature Gate)、远程配置(Feature Config)、实验分组(Experiment)
- 📡 UTM 追踪:自动解析营销渠道参数并完成首次归因
- 💾 离线持久化:事件队列持久化存储、批量上报、失败重试,保障数据不丢
- 💥 原生崩溃采集:App 端配合 sensorswave-crash UTS 插件 自动采集原生崩溃(fatal),下次启动回放上报,Android / iOS / HarmonyOS 一个插件全覆盖
- 🔌 插件化架构:内置插件管理器,支持自定义采集插件扩展能力
平台支持
| 平台 | platform | lib 标识 | 自动采集事件 |
| --- | --- | --- | --- |
| 微信小程序 | miniProgram | wechatMini | $MPLaunch $MPShow $MPHide $MPPageView $MPPageLeave $MPShare |
| 支付宝小程序 | miniProgram | alipayMini | 同上 |
| 抖音小程序 | miniProgram | douyinMini | 同上 |
| 百度小程序 | miniProgram | baiduMini | 同上 |
| App iOS | app | ios | $AppStart $AppEnd $AppPageView $AppPageLeave $AppInstall |
| App Android | app | android | 同上 |
| App HarmonyOS | app | harmony | 同上 |
| H5 | h5 | webjs | $PageView $PageLoad $PageLeave $WebClick |
平台在运行时通过 uni.getSystemInfoSync() 自动识别,无需手动指定。
安装
# npm
npm install @sensorswave/uniapp-sdk
# yarn
yarn add @sensorswave/uniapp-sdk快速开始
UniApp 推荐通过 Vue 插件 接入,插件会在 app.use() 时自动完成初始化,并注入页面生命周期 mixin,实现无侵入的页面浏览 / 离开采集。
Vue 3(推荐)
import { createSSRApp } from 'vue'
import App from './App.vue'
import Sensorswave from '@sensorswave/uniapp-sdk'
export function createApp() {
const app = createSSRApp(App)
// 安装插件:自动初始化 SDK + 注入页面生命周期 mixin
app.use(Sensorswave.plugin, {
sourceToken: 'your-source-token',
apiHost: 'https://your-api-host.com',
autoCapture: true,
batchSend: false,
debug: false,
})
return { app }
}Vue 2
import Vue from 'vue'
import App from './App.vue'
import Sensorswave from '@sensorswave/uniapp-sdk'
App.mpType = 'app'
Vue.use(Sensorswave.plugin, {
sourceToken: 'your-source-token',
apiHost: 'https://your-api-host.com',
autoCapture: true,
batchSend: false,
debug: false,
})
const app = new Vue({ ...App })
app.$mount()在组件中使用
插件安装后,SDK 实例会挂载到 Vue 全局属性 $sensors。需要特别说明的是:
this.$sensors只在「Options API」中可用(Vue2 的methods/data,或 Vue3 的export default { methods: {} })。- Vue3 的
<script setup>没有this,不能写this.$sensors,需改用下面的写法。 - 无论哪种写法,拿到的是同一个 SDK 单例,调用方式完全一致。
Vue 2 / Options API
<script>
export default {
methods: {
onSubmit() {
this.$sensors.trackEvent('OrderSubmit', {
order_id: 'ORDER_001',
amount: 99.9,
})
},
},
}
</script>Vue 3 <script setup>(推荐:直接 import 单例)
由于 Sensorswave 默认导出本身就是单例,最简单的方式是直接 import 调用,无需依赖 this:
<script setup>
import Sensorswave from '@sensorswave/uniapp-sdk'
function onSubmit() {
Sensorswave.trackEvent('OrderSubmit', {
order_id: 'ORDER_001',
amount: 99.9,
})
}
</script>手动初始化(不使用插件)
如果你不需要自动页面生命周期采集,或希望完全自己控制初始化时机,可以直接调用 init:
import Sensorswave from '@sensorswave/uniapp-sdk'
Sensorswave.init('your-source-token', {
apiHost: 'https://your-api-host.com',
autoCapture: true,
batchSend: false,
})
// 上报自定义事件
Sensorswave.trackEvent('ButtonClick', {
button_name: 'submit',
page: 'home',
})⚠️
sourceToken和apiHost为必填项,缺失会导致数据无法上报(SDK 会在控制台输出告警)。
配置项
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| sourceToken | string | — | 必填。数据上报凭证,由服务端分配 |
| apiHost | string | '' | 必填。数据上报 API 地址 |
| debug | boolean | false | 是否开启调试模式,输出详细日志 |
| autoCapture | boolean | true | 是否开启自动采集(页面浏览、启动等) |
| batchSend | boolean | false | 是否启用批量发送(攒够一批或定时 flush) |
| enableAB | boolean | false | 是否启用 A/B 测试功能 |
| enableClickTrack | boolean | false | 是否启用 H5 元素点击采集($WebClick),仅 H5 平台生效 |
| enableShareTrack | boolean | true | 是否启用小程序分享采集 |
| enableErrorTrack | boolean | false | 是否启用异常自动采集($Exception,error 级)。独立于 autoCapture 生效;手动上报 trackException 不受此开关限制。各端覆盖边界见异常采集 |
| enableCrashTrack | boolean | false | 是否启用原生崩溃(fatal)采集,仅 App 平台生效。需配合 sensorswave-crash UTS 插件(按插件市场页面的说明安装到工程 uni_modules/,App 入口静态 import 注入 provider,见原生崩溃采集) |
| abRefreshInterval | number | 600000 | A/B 测试缓存刷新间隔(毫秒,默认 10 分钟,最小 30 秒) |
| optOutCapturing | boolean | false | 是否禁用采集(合规开关)。为 true 时 SDK 进入 opt-out 状态:不采集任何事件属性、不写队列、不上报。适用于「用户授权前不采集」的合规场景 |
| persistOptOut | boolean | false | 是否本地持久化 opt-out 状态。为 true 时,opt-out / opt-in 的决策会写入平台存储,跨会话(destroy + 重新 init)保留;默认仅存内存,每次 init 按 optOutCapturing 重新判定 |
📌 opt-out 状态初始化优先级:
init()时按以下顺序判定是否进入禁用态 —— ① init 之前已调用optOutCapturing()→ 保留;②optOutCapturing: true→ 强制禁用;③persistOptOut: true且本地存有持久化的禁用值 → 恢复。
API 方法
事件追踪
trackEvent(eventName, properties?)
手动上报一个自定义事件。
eventName(string, 必填):事件名称properties(object, 可选):事件属性
Sensorswave.trackEvent('OrderSubmit', {
order_id: 'ORDER_001',
amount: 99.9,
currency: 'CNY',
})track(event)
以完整的原始事件对象上报,可覆盖 time、trace_id、login_id、user_properties 等字段。
Sensorswave.track({
event: 'PurchaseCompleted',
properties: {
product_id: '12345',
amount: 99.99,
},
time: Date.now(),
trace_id: 'unique-trace-id-12345',
login_id: 'user_12345',
user_properties: {
plan: 'premium',
},
})用户标识
identify(loginId)
设置登录 ID,并发送 $Identify 事件,将匿名行为与登录用户关联。
Sensorswave.identify('user_12345')setLoginId(loginId)
设置登录 ID,不发送 $Identify 事件。仅需要标识用户、不需要追踪关联事件时使用。
Sensorswave.setLoginId('user_12345')getLoginId()
获取当前登录用户 ID。返回 string。
const loginId = Sensorswave.getLoginId()getAnonId()
获取当前匿名用户 ID,该 ID 在设备首次使用时自动生成并持久化。返回 string。
const anonId = Sensorswave.getAnonId()reset(resetAnonId?)
用户登出时调用,解除登录 ID 与设备的绑定。
// 用户登出时
Sensorswave.reset() // 默认保留匿名 ID
// 如果需要同时重置匿名 ID(如公共设备场景)
Sensorswave.reset(true)reset():登出,保留匿名 ID(默认行为),登出后的行为继续挂在原设备匿名 ID 下reset(true):登出并重置匿名 ID,生成新的匿名 ID,登出后的行为与登出前的历史行为断开
调用后后续事件不再携带旧的 login_id;仅清除本地身份状态,不上报事件。同时会重置会话:后续事件携带全新的 $session_id。
会话(Session)
每条事件的 properties 中都会自动携带 $session_id(UUID v7 格式,统一小写,前 48 bit 为毫秒级时间戳),由 SDK 自动管理,无需(也无法通过事件属性)手动设置。
满足以下任一条件时,SDK 会开启新的 session:
- 首次访问
- 距最近一次事件活动空闲超过 30 分钟
- 当前 session 持续时长达到 12 小时(即使一直有活动)
- 调用
reset()(用户登出)
多个标签页/窗口共享同一个 session:H5 平台通过持久化 cookie 共享(并备份到本地存储),小程序/App 平台通过平台本地存储共享。
getSessionId()
获取当前会话 ID,与事件的 properties.$session_id 同源。返回 string(未初始化时返回空字符串)。只读查询:会话有效时不推进活跃时间(发事件才会续期 30 分钟空闲窗口),仅在满足轮换条件时返回新会话 ID。
const sessionId = Sensorswave.getSessionId()用户属性(Profile)
profileSet(properties)
设置用户属性,已存在的属性会被覆盖。
Sensorswave.profileSet({
name: '张三',
age: 30,
plan: 'premium',
})profileSetOnce(properties)
首次设置用户属性,已存在的属性不会被覆盖。常用于记录首次注册时间、首次来源等。
Sensorswave.profileSetOnce({
signup_date: '2026-01-15',
initial_referrer: 'google',
})profileIncrement(properties)
对数值型用户属性进行递增操作,仅支持数值类型。
// 递增单个属性
Sensorswave.profileIncrement({ login_count: 1 })
// 递增多个属性
Sensorswave.profileIncrement({
login_count: 1,
points_earned: 100,
purchases_count: 1,
})profileAppend(properties)
向数组类型的用户属性追加元素,不去重。
Sensorswave.profileAppend({
categories_viewed: ['electronics', 'mobile_phones'],
})profileUnion(properties)
向数组类型的用户属性追加元素,自动去重。
Sensorswave.profileUnion({
interests: ['technology', 'gaming'],
})profileUnset(keys)
删除指定的用户属性。
keys(string[], 必填):要删除的属性名数组
// 删除单个属性
Sensorswave.profileUnset(['temporary_campaign'])
// 删除多个属性
Sensorswave.profileUnset(['old_plan', 'expired_flag'])profileDelete()
删除当前用户的全部用户属性,操作不可恢复。仅对已登录用户有效。
Sensorswave.profileDelete()公共属性
公共属性会自动附加到后续上报的所有事件上,适合注入全局上下文(如应用版本、环境等)。
registerCommonProperties(properties)
注册静态或动态公共属性。值可以是字符串等静态值,也可以是「每次事件上报时动态求值」的函数。
Sensorswave.registerCommonProperties({
// 静态属性
app_version: '1.0.0',
environment: 'production',
// 动态属性(每次上报事件时都会重新求值)
current_time: () => Date.now(),
user_tier: () => getUserTier(),
})clearCommonProperties(keys?)
清除已注册的公共属性。不传 keys 时清除全部。
// 清除指定公共属性
Sensorswave.clearCommonProperties(['app_version', 'user_tier'])
// 清除全部公共属性
Sensorswave.clearCommonProperties()getCommonProperties()
获取当前已注册公共属性的只读快照。
const props = Sensorswave.getCommonProperties()A/B 测试
需在初始化时设置
enableAB: true,否则以下方法将直接返回默认值。
checkFeatureGate(key)
检查功能开关(Feature Gate)是否对当前用户开启。返回 Promise<boolean>。
const enabled = await Sensorswave.checkFeatureGate('new_checkout_flow')
if (enabled) {
showNewCheckout()
}getFeatureConfig(key)
获取远程配置(Feature Config)。服务端返回的 JSON 字符串会被自动解析。返回 Promise<Record<string, unknown>>,默认 {}。
const config = await Sensorswave.getFeatureConfig('app_settings')
const { theme, layout } = configgetExperiment(key)
获取实验分组(Experiment)。返回 Promise<Record<string, unknown>>。
const exp = await Sensorswave.getExperiment('homepage_layout')
const { layout_type } = expisInitialized()
判断 SDK 是否已经 init() 完成。返回 boolean,未初始化时为 false。
if (Sensorswave.isInitialized()) {
Sensorswave.trackEvent('Ready')
}合规(opt-out / opt-in)
用于满足「用户授权前不采集」等隐私合规场景。进入 opt-out 状态后,SDK 不采集任何事件属性、不写队列、不上报,对外 API 调用均静默早退(不报错);已入队但尚未发送的事件会暂停派发(不删队列、不动存储)。
optOutCapturing()
禁用采集,使 SDK 进入 opt-out 状态。可在 init() 之前调用,状态会保留到 init() 时参与优先级判定。
Sensorswave.optOutCapturing()optInCapturing()
允许采集,取消 opt-out 状态。会恢复批量发送器派发,并补发 opt-out 期间被暂缓的 UTM 首次归因;其它在禁用期间发生的事件不会补发。
Sensorswave.optInCapturing()hasOptedOutCapturing()
查询当前是否已禁用采集。返回 boolean(true = 已禁用)。可在 init() 之前调用。
const optedOut = Sensorswave.hasOptedOutCapturing()
if (optedOut) {
console.log('采集已禁用')
}💡 典型用法:在隐私授权弹窗处根据用户选择调用
optOutCapturing()/optInCapturing(),并用hasOptedOutCapturing()回显开关状态。若希望用户的选择在应用重启后依然生效,初始化时设置persistOptOut: true。// 用户拒绝授权 → 停止采集 Sensorswave.optOutCapturing() // 用户同意授权 → 恢复采集 Sensorswave.optInCapturing()
自动采集事件
开启 autoCapture(默认开启)后,SDK 会根据运行平台自动注册对应采集器:
- 小程序:启动、显示/隐藏、页面浏览/离开、(默认)分享
- App:启动/退出、页面浏览/离开
- H5:页面浏览、页面加载、页面离开、(可选)点击
页面浏览 / 离开采集通过 Vue 插件注入的页面生命周期 mixin 驱动(onShow / onHide / onUnload),无需手动埋点。
异常采集($Exception)
开启 enableErrorTrack: true 后,SDK 自动采集运行期 JS 异常并上报为 $Exception 事件($exception_level='error',含 $exception_type / $exception_message / $exception_frames 结构化堆栈帧)。内置两层保护:指纹去重(2s 内相同 type+message 只报第一条,防同一错误双通道重复上报)与会话内限流(同 type 最多 10 条、总计 30 条,防异常风暴刷量)。
已捕获的错误用手动上报兜底:Sensorswave.trackException(throwable, properties?)——接受 Error 实例、错误字符串(uni.onError 格式)、{ errMsg } 对象等;不受 enableErrorTrack 开关限制、不限流、不去重。
各端通道与覆盖边界
| 平台 | 自动采集通道 | 覆盖 | 不覆盖 |
| --- | --- | --- | --- |
| H5 | window error + unhandledrejection | 全部 JS 异常(含异步) | 资源加载错误(img/script 等) |
| 微信等小程序 | uni.onError + uni.onUnhandledRejection(引擎原生接线) | 未捕获异常 + 未处理 Promise 拒绝 | — |
| 支付宝小程序 | 仅 uni.onUnhandledRejection(无 uni.onError,静默降级) | 未处理 Promise 拒绝 | 未捕获异常 |
| App(Android/iOS,app-vue) | uni.onError(经 Vue errorHandler 派发) | Vue 可达错误:渲染 / 生命周期 / 事件回调(含 async 事件 handler) | 逃出 Vue 调用栈的 setTimeout 裸抛、裸 Promise.reject |
📌 App 端边界的依据:app-vue(vue3)运行时里
ON_ERROR生命周期钩子的唯一派发方是 Vueapp.config.errorHandler(uni.onError/ App.vueonError/ mixinonError三者同链);逃出 Vue 的异步错误走原生 Uncaught Exception 通路、不回流到任何 JS 钩子,且uni.onUnhandledRejection在 Android App 端可注册但无派发方(引擎未接线)。DCloud 官方 uni 统计在 App 端的错误采集边界与此一致——这是 uni-app(vue)官方通道的既定能力范围,不是本 SDK 的缺陷。
App 端异步异常的兜底规范(trackException)
官方通道在 App 端覆盖不到异步逃逸错误,业务侧应在异步任务入口 catch 后手动上报:
// 异步任务入口:try/catch 后走 trackException(uni-app 生态通行做法)
setTimeout(() => {
try {
doAsyncWork();
} catch (e) {
Sensorswave.trackException(e, { scene: 'async_task' });
}
}, 0);
// Promise 链:链尾统一 .catch 兜底,比逐处 try/catch 更可维护
fetchData()
.then(render)
.catch((e) => Sensorswave.trackException(e, { scene: 'data_load' }));💡 若业务已在
main.js配置了 Vue 全局errorHandler(推荐做法),Vue 可达的错误会被转发到trackException;SDK 的自动采集与该转发由指纹去重合并,不会重复上报同一条错误。
原生崩溃采集(App 端)
开启 enableCrashTrack: true 后,SDK 会在 App 端采集原生崩溃(fatal):崩溃瞬间由原生插件将规范化记录落盘到设备本地,下次启动时 SDK 拉取回放为 $Exception 事件($exception_level='fatal'、time 回填崩溃时刻),入队成功后删除设备记录,重启不会重复上报。
这与 JS 层 trackException 上报的运行期异常(error 级)互补:原生崩溃发生时进程随即死亡(Java 未捕获异常 / NSException / ArkTS 异常),只能靠「落盘 + 下次启动回放」捕获。
接入步骤
- 安装 UTS 插件:按插件市场页面 sensorswave-crash 的说明安装到工程
uni_modules/ - init 配置开启
enableCrashTrack: true - App 入口注入 provider(见下方「手动注入」):
main.js的#ifdef APP下静态 import 插件模块并registerCrashProvider,三行代码,崩溃回放先于本次会话$AppStart
为什么需要入口注入,而不是 SDK 全自动解析? uni-app(vue)工程 iOS 端运行时按 '@/uni_modules/<id>' 路径解析不到本地 UTS 插件(实测 uni.requireUTSPlugin 报 is not found——该端静态 import 在编译期生成 initUTSProxyFunction 代理桥,不经 requireUTSPlugin 的运行时注册表),iOS 上自动通道无法工作;Android / HarmonyOS 的自动通道保留为尽力而为的兜底(可用性随 HBuilderX 版本浮动,未逐版本验证)。SDK 自身也不能静态 import 插件——那会让所有未拷插件的工程在编译期 resolve 失败(可选依赖必须由安装方 import)。因此「入口三行注入」是各端统一、不依赖运行时查找的可靠接法。自动通道失败时(且未手动注入)init 打 warn 日志提示接入指引(不受 debug 开关门控),采集能力静默降级,其余功能不受影响。
手动注入(iOS 必做,各端推荐统一使用)
在 main.js 的 #ifdef APP 下静态 import 模块后注入,覆盖自动通道:
// #ifdef APP
import * as crashUts from '@/uni_modules/sensorswave-crash'
import { createUtsCrashProvider } from '@sensorswave/uniapp-sdk'
// #endif
export function createApp() {
const app = createSSRApp(App)
// #ifdef APP
// 须在 app.use(触发 SDK init)之前:崩溃回放先于本次会话 $AppStart
// ⚠️ pull/ack 两个导出须以对象字面量逐成员引用后传入:UTS 代理按「JS 是否引用」
// 生成并摇树(namespace 直传会被摇掉),漏引用 ackCrashRecords 会导致回放能
// 拉到记录但删除静默失效(重启重复上报)——注入时 SDK 会对缺失方法打 warn 兜底
Sensorswave.registerCrashProvider(createUtsCrashProvider({
getPendingCrashRecords: crashUts.getPendingCrashRecords,
ackCrashRecords: crashUts.ackCrashRecords,
}))
// #endif
app.use(Sensorswave.plugin, pluginConfig)
// ...
}📌 注入时机:
init之前调用最佳(崩溃回放先于本次会话$AppStart入队);init之后调用也可,SDK 会立即用注入的 provider 重放一次(已 ack 的记录拉取为空,不会重复上报)。注入对象跨destroy()保留。
覆盖范围(已知局限)
| 平台 | 覆盖 | 不覆盖 |
| --- | --- | --- |
| Android | Java / Kotlin 未捕获异常 | NDK / C++ 崩溃、SIGSEGV 等信号级 |
| iOS | ObjC / Swift NSException | BSD 信号级崩溃(Swift fatalError / force-unwrap 等);Xcode 调试器附加时不捕获 |
| HarmonyOS | ArkTS 未捕获异常、C++ 崩溃、卡死(APP_FREEZE) | — |
纯 UTS 插件无法移植 xCrash / PLCrashReporter 的信号级捕获机制,以上为已确认的设计取舍。插件侧细节(桥契约、存储与可靠性、去重语义)见 uni_modules/sensorswave-crash/README.md。
隐私:opt-out 期间崩溃记录保留在设备本地(TTL 7 天、上限 100 条),不拉取不删除,恢复授权后自动补报一次。
预置属性
SDK 会自动采集一系列以 $ 开头的预置属性,包括设备信息($os、$model、$screen_width 等)、运行环境($lib、$lib_version)、网络($network_type)、页面信息($url、$url_path 等)以及 UTM 渠道参数。
⚠️
$前缀为系统保留,自定义属性请勿使用$前缀,以免与预置属性冲突。
HarmonyOS 适配
App 鸿蒙端需要声明网络与网络信息相关权限,否则 $network_type 等属性无法正常采集:
// entry/src/main/module.json5
{
"module": {
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.GET_NETWORK_INFO" }
]
}
}⚠️ 关键说明:必须配置
ohos.permission.GET_NETWORK_INFO权限才能正确获取网络信息鸿蒙平台上,SDK 通过
uni.getNetworkType()获取当前网络类型以采集$network_type预置属性。若未声明ohos.permission.GET_NETWORK_INFO权限,uni.getNetworkType()将调用失败,导致$network_type始终为空。这是鸿蒙端正确获取网络信息的前提,请务必在module.json5中声明该权限。各权限作用:
ohos.permission.INTERNET:保证事件数据能正常上报ohos.permission.GET_NETWORK_INFO:保证$network_type能正确获取
License
Apache-2.0
