80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
/**
|
||
* 事件上报服务
|
||
*
|
||
* LandingSDK 由 index.html 异步加载,加载完成前 window.tracker 只是一个排队用的桩函数;
|
||
* v2 SDK 不再接受桩队列回放出来的泛型调用 track({ event })(会判「非法上报」丢弃),
|
||
* 所以这里等 SDK 就绪后再走具名方法 landingPageView / landingPageClick。
|
||
*/
|
||
import type { EventCode } from './types'
|
||
|
||
const SDK_READY_TIMEOUT_MS = 10000
|
||
const SDK_POLL_MS = 30
|
||
|
||
type LandingTracker = ((payload: Record<string, any>) => Promise<unknown>) & {
|
||
landingPageView?: (properties?: Record<string, any>) => Promise<unknown>
|
||
landingPageClick?: (properties?: Record<string, any>) => Promise<unknown>
|
||
}
|
||
|
||
// 等到 window.tracker 被 App.vue initTracker 替换成真正的 SDK track(带具名方法)为止。
|
||
// 注意:SDK 的 track 是 Proxy,访问任意属性(包括 then)都会被当成事件名,所以不能直接把它交给 Promise resolve,
|
||
// 否则 Promise 会去调它的 then 而永远不落定;这里用对象包一层。
|
||
function waitForTracker(): Promise<{ tracker: LandingTracker | null }> {
|
||
return new Promise((resolve) => {
|
||
const startedAt = Date.now()
|
||
const check = () => {
|
||
const tracker = (window as any).tracker as LandingTracker | undefined
|
||
if (tracker && typeof tracker.landingPageView === 'function') {
|
||
resolve({ tracker })
|
||
return
|
||
}
|
||
if (Date.now() - startedAt > SDK_READY_TIMEOUT_MS) {
|
||
resolve({ tracker: null })
|
||
return
|
||
}
|
||
setTimeout(check, SDK_POLL_MS)
|
||
}
|
||
check()
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 事件上报服务类
|
||
*/
|
||
export class EventTracker {
|
||
|
||
/**
|
||
* 发送事件到服务器
|
||
*/
|
||
async sendEvent(eventCode: EventCode, properties?: Record<string, any>) {
|
||
const { tracker } = await waitForTracker()
|
||
if (!tracker) {
|
||
console.log('LandingSDK 未就绪,事件丢弃:' + eventCode)
|
||
return
|
||
}
|
||
try {
|
||
if (eventCode === 'landing_page_view' && typeof tracker.landingPageView === 'function') {
|
||
await tracker.landingPageView(properties)
|
||
} else if (eventCode === 'landing_page_click' && typeof tracker.landingPageClick === 'function') {
|
||
await tracker.landingPageClick(properties)
|
||
} else {
|
||
await tracker({ event: eventCode, ...properties })
|
||
}
|
||
} catch (err) {
|
||
console.log('埋点上报失败:' + eventCode + ' ' + err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 单例实例
|
||
let trackerInstance: EventTracker | null = null
|
||
|
||
/**
|
||
* 获取事件上报器实例(自动初始化,无需手动初始化)
|
||
*/
|
||
export function getEventTracker(): EventTracker {
|
||
if (!trackerInstance) {
|
||
trackerInstance = new EventTracker()
|
||
}
|
||
return trackerInstance
|
||
}
|