@rk911/tracker-sdk 1.7.17 → 1.7.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +554 -531
- package/native/ios/RKTrackerSDK.swift +1060 -944
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,531 +1,554 @@
|
|
|
1
|
-
import { Tracker } from './core/tracker.js'
|
|
2
|
-
import {
|
|
3
|
-
createDadianProxy,
|
|
4
|
-
createImpressionDirective,
|
|
5
|
-
createPageStayMixin,
|
|
6
|
-
} from './core/compat.js'
|
|
7
|
-
import { WebAdapter } from './platforms/web.js'
|
|
8
|
-
import { WeappAdapter } from './platforms/weapp.js'
|
|
9
|
-
import { AppAdapter } from './platforms/app.js'
|
|
10
|
-
import { getProjectRules } from './customize/index.js'
|
|
11
|
-
|
|
12
|
-
// 插件注册表
|
|
13
|
-
import { PageViewPlugin } from './plugins/page-view.js'
|
|
14
|
-
import { ClickPlugin } from './plugins/click.js'
|
|
15
|
-
import { PageStayPlugin } from './plugins/page-stay.js'
|
|
16
|
-
import { ExposurePlugin } from './plugins/exposure.js'
|
|
17
|
-
import { JsErrorPlugin } from './plugins/js-error.js'
|
|
18
|
-
import { ApiWatchPlugin } from './plugins/api-watch.js'
|
|
19
|
-
import { AppLifecyclePlugin } from './plugins/app-lifecycle.js'
|
|
20
|
-
import { PerformancePlugin } from './plugins/performance.js'
|
|
21
|
-
import { ScrollDepthPlugin } from './plugins/scroll-depth.js'
|
|
22
|
-
|
|
23
|
-
const PLUGIN_MAP = {
|
|
24
|
-
'page-view': PageViewPlugin,
|
|
25
|
-
click: ClickPlugin,
|
|
26
|
-
'page-stay': PageStayPlugin,
|
|
27
|
-
exposure: ExposurePlugin,
|
|
28
|
-
'js-error': JsErrorPlugin,
|
|
29
|
-
performance: PerformancePlugin,
|
|
30
|
-
'scroll-depth': ScrollDepthPlugin,
|
|
31
|
-
'api-watch': ApiWatchPlugin,
|
|
32
|
-
'app-lifecycle': AppLifecyclePlugin,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 单例
|
|
36
|
-
let trackerInstance = null
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 自动检测当前运行平台
|
|
40
|
-
*/
|
|
41
|
-
function detectPlatform() {
|
|
42
|
-
try {
|
|
43
|
-
if (
|
|
44
|
-
typeof wx !== 'undefined' &&
|
|
45
|
-
typeof wx.getSystemInfoSync === 'function' &&
|
|
46
|
-
typeof document === 'undefined'
|
|
47
|
-
) {
|
|
48
|
-
return 'weapp'
|
|
49
|
-
}
|
|
50
|
-
} catch (e) {}
|
|
51
|
-
|
|
52
|
-
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
53
|
-
return 'web'
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return 'web'
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* 创建平台适配器
|
|
61
|
-
*/
|
|
62
|
-
function createAdapter(platform, config) {
|
|
63
|
-
switch (platform) {
|
|
64
|
-
case 'weapp':
|
|
65
|
-
return new WeappAdapter(config)
|
|
66
|
-
case 'app':
|
|
67
|
-
return new AppAdapter(config)
|
|
68
|
-
default:
|
|
69
|
-
return new WebAdapter(config)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* 创建 Tracker 实例
|
|
75
|
-
*/
|
|
76
|
-
export function createTracker(config) {
|
|
77
|
-
if (trackerInstance) {
|
|
78
|
-
trackerInstance.destroy()
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
var platform = config.platform || detectPlatform()
|
|
82
|
-
config.platform = platform
|
|
83
|
-
|
|
84
|
-
// 自动加载项目定制规则(未显式传入时按 appId 匹配)
|
|
85
|
-
if (!config.projectRules && config.appId) {
|
|
86
|
-
config.projectRules = getProjectRules(config.appId)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
var adapter = createAdapter(platform, config)
|
|
90
|
-
trackerInstance = new Tracker(config, adapter)
|
|
91
|
-
|
|
92
|
-
// 触发 onBeforeInit 钩子(必须在插件注册与首屏事件触发前执行,以便同步/异步注入 Native 上下文)
|
|
93
|
-
if (typeof config.onBeforeInit === 'function') {
|
|
94
|
-
try {
|
|
95
|
-
config.onBeforeInit(trackerInstance)
|
|
96
|
-
} catch (e) {
|
|
97
|
-
console.warn('[TrackerSDK] onBeforeInit error:', e)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// 注册插件
|
|
102
|
-
var pluginNames = config.plugins || []
|
|
103
|
-
pluginNames.forEach(function (pluginName) {
|
|
104
|
-
var PluginClass = null
|
|
105
|
-
|
|
106
|
-
if (typeof pluginName === 'string') {
|
|
107
|
-
PluginClass = PLUGIN_MAP[pluginName]
|
|
108
|
-
} else if (typeof pluginName === 'function') {
|
|
109
|
-
PluginClass = pluginName
|
|
110
|
-
} else if (pluginName && typeof pluginName.install === 'function') {
|
|
111
|
-
// 直接传入插件实例
|
|
112
|
-
trackerInstance.use(pluginName)
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (PluginClass) {
|
|
117
|
-
trackerInstance.use(new PluginClass())
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
return trackerInstance
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 获取当前 Tracker 单例
|
|
126
|
-
*/
|
|
127
|
-
export function getTracker() {
|
|
128
|
-
return trackerInstance
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* 设备注册 — 无 deviceId 时调用后端接口获取,失败重试最多 3 次
|
|
133
|
-
*/
|
|
134
|
-
function registerDeviceIfNeeded(tracker) {
|
|
135
|
-
var adapter = tracker.getAdapter()
|
|
136
|
-
var config = tracker.getConfig()
|
|
137
|
-
var session = tracker.getSession()
|
|
138
|
-
|
|
139
|
-
// 如果是由 App/Native 动态注入的临时 DeviceID,跳过 H5 本地注册逻辑
|
|
140
|
-
if (session.isNativeDeviceId && session.getDeviceId()) {
|
|
141
|
-
return
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
var existingId = session.getDeviceId() || adapter.getStorage('deviceId') || adapter.getStorage('_tracker_device_id')
|
|
145
|
-
if (existingId) {
|
|
146
|
-
tracker.setDeviceId(existingId)
|
|
147
|
-
return
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// [PROJECT-ADAPT] appId 默认值
|
|
151
|
-
// 店长端 appId=1, apph5 appId=12/13, 小程序 appId=18
|
|
152
|
-
// 各项目在初始化时传入自己的 appId,此处 ||1 为兜底
|
|
153
|
-
var deviceInfo = { appId: Number(config.appId) || 1, id: '' }
|
|
154
|
-
if (typeof adapter.getDetailedDeviceInfo === 'function') {
|
|
155
|
-
Object.assign(deviceInfo, adapter.getDetailedDeviceInfo())
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// [PROJECT-ADAPT] 设备注册请求头
|
|
159
|
-
// 当前网关要求 appId/deviceId 作为自定义 header
|
|
160
|
-
var header = { 'content-type': 'application/json', appId: String(config.appId), deviceId: '' }
|
|
161
|
-
if (typeof config.getAuthHeaders === 'function') {
|
|
162
|
-
Object.assign(header, config.getAuthHeaders() || {})
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
var registerUrl = config.dadianUrl + (config.deviceRegister.url || '/api/point/device/add')
|
|
166
|
-
var maxRetries = 3
|
|
167
|
-
|
|
168
|
-
function attempt(retryCount) {
|
|
169
|
-
adapter
|
|
170
|
-
.request({ method: 'POST', url: registerUrl, data: deviceInfo, header: header })
|
|
171
|
-
.then(function (res) {
|
|
172
|
-
if (res && res.data && res.data.code === 0 && res.data.data) {
|
|
173
|
-
adapter.setStorage('deviceId', res.data.data)
|
|
174
|
-
tracker.setDeviceId(res.data.data)
|
|
175
|
-
if (config.debug) {
|
|
176
|
-
console.log('[TrackerSDK] device registered:', res.data.data)
|
|
177
|
-
}
|
|
178
|
-
} else if (retryCount < maxRetries) {
|
|
179
|
-
var delay = 1000 * Math.pow(2, retryCount)
|
|
180
|
-
setTimeout(function () { attempt(retryCount + 1) }, delay)
|
|
181
|
-
if (config.debug) {
|
|
182
|
-
console.warn('[TrackerSDK] device register bad response, retry ' + (retryCount + 1) + '/' + maxRetries + ' in ' + delay + 'ms')
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
.catch(function (err) {
|
|
187
|
-
if (retryCount < maxRetries) {
|
|
188
|
-
var delay = 1000 * Math.pow(2, retryCount)
|
|
189
|
-
setTimeout(function () { attempt(retryCount + 1) }, delay)
|
|
190
|
-
if (config.debug) {
|
|
191
|
-
console.warn('[TrackerSDK] device register failed, retry ' + (retryCount + 1) + '/' + maxRetries + ' in ' + delay + 'ms')
|
|
192
|
-
}
|
|
193
|
-
} else if (config.debug) {
|
|
194
|
-
console.warn('[TrackerSDK] device register failed after ' + maxRetries + ' retries:', err)
|
|
195
|
-
}
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
attempt(0)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function _parseJwtPayload(token) {
|
|
203
|
-
if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null
|
|
204
|
-
try {
|
|
205
|
-
var tokenParts = token.split('.')
|
|
206
|
-
if (tokenParts.length >= 2) {
|
|
207
|
-
var base64Url = tokenParts[1]
|
|
208
|
-
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
|
|
209
|
-
var decodedStr = ''
|
|
210
|
-
if (typeof atob === 'function') {
|
|
211
|
-
try {
|
|
212
|
-
decodedStr = decodeURIComponent(escape(atob(base64)))
|
|
213
|
-
} catch (e1) {
|
|
214
|
-
decodedStr = atob(base64)
|
|
215
|
-
}
|
|
216
|
-
} else if (typeof Buffer !== 'undefined') {
|
|
217
|
-
decodedStr = Buffer.from(base64, 'base64').toString('utf-8')
|
|
218
|
-
}
|
|
219
|
-
if (decodedStr) {
|
|
220
|
-
return JSON.parse(decodedStr)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
} catch (e) {}
|
|
224
|
-
return null
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* 自动检测 userId:多源交叉读取,并确保不把已有的合法身份置空
|
|
229
|
-
*/
|
|
230
|
-
function _ensureUserId(tracker) {
|
|
231
|
-
var session = tracker.getSession()
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
var
|
|
284
|
-
if (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
)
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
tracker.
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
1
|
+
import { Tracker } from './core/tracker.js'
|
|
2
|
+
import {
|
|
3
|
+
createDadianProxy,
|
|
4
|
+
createImpressionDirective,
|
|
5
|
+
createPageStayMixin,
|
|
6
|
+
} from './core/compat.js'
|
|
7
|
+
import { WebAdapter } from './platforms/web.js'
|
|
8
|
+
import { WeappAdapter } from './platforms/weapp.js'
|
|
9
|
+
import { AppAdapter } from './platforms/app.js'
|
|
10
|
+
import { getProjectRules } from './customize/index.js'
|
|
11
|
+
|
|
12
|
+
// 插件注册表
|
|
13
|
+
import { PageViewPlugin } from './plugins/page-view.js'
|
|
14
|
+
import { ClickPlugin } from './plugins/click.js'
|
|
15
|
+
import { PageStayPlugin } from './plugins/page-stay.js'
|
|
16
|
+
import { ExposurePlugin } from './plugins/exposure.js'
|
|
17
|
+
import { JsErrorPlugin } from './plugins/js-error.js'
|
|
18
|
+
import { ApiWatchPlugin } from './plugins/api-watch.js'
|
|
19
|
+
import { AppLifecyclePlugin } from './plugins/app-lifecycle.js'
|
|
20
|
+
import { PerformancePlugin } from './plugins/performance.js'
|
|
21
|
+
import { ScrollDepthPlugin } from './plugins/scroll-depth.js'
|
|
22
|
+
|
|
23
|
+
const PLUGIN_MAP = {
|
|
24
|
+
'page-view': PageViewPlugin,
|
|
25
|
+
click: ClickPlugin,
|
|
26
|
+
'page-stay': PageStayPlugin,
|
|
27
|
+
exposure: ExposurePlugin,
|
|
28
|
+
'js-error': JsErrorPlugin,
|
|
29
|
+
performance: PerformancePlugin,
|
|
30
|
+
'scroll-depth': ScrollDepthPlugin,
|
|
31
|
+
'api-watch': ApiWatchPlugin,
|
|
32
|
+
'app-lifecycle': AppLifecyclePlugin,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 单例
|
|
36
|
+
let trackerInstance = null
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 自动检测当前运行平台
|
|
40
|
+
*/
|
|
41
|
+
function detectPlatform() {
|
|
42
|
+
try {
|
|
43
|
+
if (
|
|
44
|
+
typeof wx !== 'undefined' &&
|
|
45
|
+
typeof wx.getSystemInfoSync === 'function' &&
|
|
46
|
+
typeof document === 'undefined'
|
|
47
|
+
) {
|
|
48
|
+
return 'weapp'
|
|
49
|
+
}
|
|
50
|
+
} catch (e) {}
|
|
51
|
+
|
|
52
|
+
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
53
|
+
return 'web'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return 'web'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 创建平台适配器
|
|
61
|
+
*/
|
|
62
|
+
function createAdapter(platform, config) {
|
|
63
|
+
switch (platform) {
|
|
64
|
+
case 'weapp':
|
|
65
|
+
return new WeappAdapter(config)
|
|
66
|
+
case 'app':
|
|
67
|
+
return new AppAdapter(config)
|
|
68
|
+
default:
|
|
69
|
+
return new WebAdapter(config)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 创建 Tracker 实例
|
|
75
|
+
*/
|
|
76
|
+
export function createTracker(config) {
|
|
77
|
+
if (trackerInstance) {
|
|
78
|
+
trackerInstance.destroy()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
var platform = config.platform || detectPlatform()
|
|
82
|
+
config.platform = platform
|
|
83
|
+
|
|
84
|
+
// 自动加载项目定制规则(未显式传入时按 appId 匹配)
|
|
85
|
+
if (!config.projectRules && config.appId) {
|
|
86
|
+
config.projectRules = getProjectRules(config.appId)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
var adapter = createAdapter(platform, config)
|
|
90
|
+
trackerInstance = new Tracker(config, adapter)
|
|
91
|
+
|
|
92
|
+
// 触发 onBeforeInit 钩子(必须在插件注册与首屏事件触发前执行,以便同步/异步注入 Native 上下文)
|
|
93
|
+
if (typeof config.onBeforeInit === 'function') {
|
|
94
|
+
try {
|
|
95
|
+
config.onBeforeInit(trackerInstance)
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.warn('[TrackerSDK] onBeforeInit error:', e)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 注册插件
|
|
102
|
+
var pluginNames = config.plugins || []
|
|
103
|
+
pluginNames.forEach(function (pluginName) {
|
|
104
|
+
var PluginClass = null
|
|
105
|
+
|
|
106
|
+
if (typeof pluginName === 'string') {
|
|
107
|
+
PluginClass = PLUGIN_MAP[pluginName]
|
|
108
|
+
} else if (typeof pluginName === 'function') {
|
|
109
|
+
PluginClass = pluginName
|
|
110
|
+
} else if (pluginName && typeof pluginName.install === 'function') {
|
|
111
|
+
// 直接传入插件实例
|
|
112
|
+
trackerInstance.use(pluginName)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (PluginClass) {
|
|
117
|
+
trackerInstance.use(new PluginClass())
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
return trackerInstance
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 获取当前 Tracker 单例
|
|
126
|
+
*/
|
|
127
|
+
export function getTracker() {
|
|
128
|
+
return trackerInstance
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 设备注册 — 无 deviceId 时调用后端接口获取,失败重试最多 3 次
|
|
133
|
+
*/
|
|
134
|
+
function registerDeviceIfNeeded(tracker) {
|
|
135
|
+
var adapter = tracker.getAdapter()
|
|
136
|
+
var config = tracker.getConfig()
|
|
137
|
+
var session = tracker.getSession()
|
|
138
|
+
|
|
139
|
+
// 如果是由 App/Native 动态注入的临时 DeviceID,跳过 H5 本地注册逻辑
|
|
140
|
+
if (session.isNativeDeviceId && session.getDeviceId()) {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
var existingId = session.getDeviceId() || adapter.getStorage('deviceId') || adapter.getStorage('_tracker_device_id')
|
|
145
|
+
if (existingId) {
|
|
146
|
+
tracker.setDeviceId(existingId)
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// [PROJECT-ADAPT] appId 默认值
|
|
151
|
+
// 店长端 appId=1, apph5 appId=12/13, 小程序 appId=18
|
|
152
|
+
// 各项目在初始化时传入自己的 appId,此处 ||1 为兜底
|
|
153
|
+
var deviceInfo = { appId: Number(config.appId) || 1, id: '' }
|
|
154
|
+
if (typeof adapter.getDetailedDeviceInfo === 'function') {
|
|
155
|
+
Object.assign(deviceInfo, adapter.getDetailedDeviceInfo())
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// [PROJECT-ADAPT] 设备注册请求头
|
|
159
|
+
// 当前网关要求 appId/deviceId 作为自定义 header
|
|
160
|
+
var header = { 'content-type': 'application/json', appId: String(config.appId), deviceId: '' }
|
|
161
|
+
if (typeof config.getAuthHeaders === 'function') {
|
|
162
|
+
Object.assign(header, config.getAuthHeaders() || {})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
var registerUrl = config.dadianUrl + (config.deviceRegister.url || '/api/point/device/add')
|
|
166
|
+
var maxRetries = 3
|
|
167
|
+
|
|
168
|
+
function attempt(retryCount) {
|
|
169
|
+
adapter
|
|
170
|
+
.request({ method: 'POST', url: registerUrl, data: deviceInfo, header: header })
|
|
171
|
+
.then(function (res) {
|
|
172
|
+
if (res && res.data && res.data.code === 0 && res.data.data) {
|
|
173
|
+
adapter.setStorage('deviceId', res.data.data)
|
|
174
|
+
tracker.setDeviceId(res.data.data)
|
|
175
|
+
if (config.debug) {
|
|
176
|
+
console.log('[TrackerSDK] device registered:', res.data.data)
|
|
177
|
+
}
|
|
178
|
+
} else if (retryCount < maxRetries) {
|
|
179
|
+
var delay = 1000 * Math.pow(2, retryCount)
|
|
180
|
+
setTimeout(function () { attempt(retryCount + 1) }, delay)
|
|
181
|
+
if (config.debug) {
|
|
182
|
+
console.warn('[TrackerSDK] device register bad response, retry ' + (retryCount + 1) + '/' + maxRetries + ' in ' + delay + 'ms')
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
.catch(function (err) {
|
|
187
|
+
if (retryCount < maxRetries) {
|
|
188
|
+
var delay = 1000 * Math.pow(2, retryCount)
|
|
189
|
+
setTimeout(function () { attempt(retryCount + 1) }, delay)
|
|
190
|
+
if (config.debug) {
|
|
191
|
+
console.warn('[TrackerSDK] device register failed, retry ' + (retryCount + 1) + '/' + maxRetries + ' in ' + delay + 'ms')
|
|
192
|
+
}
|
|
193
|
+
} else if (config.debug) {
|
|
194
|
+
console.warn('[TrackerSDK] device register failed after ' + maxRetries + ' retries:', err)
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
attempt(0)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function _parseJwtPayload(token) {
|
|
203
|
+
if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null
|
|
204
|
+
try {
|
|
205
|
+
var tokenParts = token.split('.')
|
|
206
|
+
if (tokenParts.length >= 2) {
|
|
207
|
+
var base64Url = tokenParts[1]
|
|
208
|
+
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
|
|
209
|
+
var decodedStr = ''
|
|
210
|
+
if (typeof atob === 'function') {
|
|
211
|
+
try {
|
|
212
|
+
decodedStr = decodeURIComponent(escape(atob(base64)))
|
|
213
|
+
} catch (e1) {
|
|
214
|
+
decodedStr = atob(base64)
|
|
215
|
+
}
|
|
216
|
+
} else if (typeof Buffer !== 'undefined') {
|
|
217
|
+
decodedStr = Buffer.from(base64, 'base64').toString('utf-8')
|
|
218
|
+
}
|
|
219
|
+
if (decodedStr) {
|
|
220
|
+
return JSON.parse(decodedStr)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} catch (e) {}
|
|
224
|
+
return null
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* 自动检测 userId:多源交叉读取,并确保不把已有的合法身份置空
|
|
229
|
+
*/
|
|
230
|
+
function _ensureUserId(tracker) {
|
|
231
|
+
var session = tracker.getSession()
|
|
232
|
+
|
|
233
|
+
// 0. 跨端会话必须无条件同步序列号(无论是否已有 Native 用户身份,绝对不能被提前 return 阻断)
|
|
234
|
+
if (typeof window !== 'undefined') {
|
|
235
|
+
try {
|
|
236
|
+
if (window.location) {
|
|
237
|
+
var search = (window.location.search || '') + '&' + (window.location.hash || '')
|
|
238
|
+
var matchSeq = search.match(/[?&]_trk_seq=([^&]+)/)
|
|
239
|
+
if (matchSeq) {
|
|
240
|
+
session.setInitialSequence(Number(decodeURIComponent(matchSeq[1])))
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (window.NativeBridge && typeof window.NativeBridge.getNativeContextSync === 'function') {
|
|
244
|
+
var nativeRaw = window.NativeBridge.getNativeContextSync()
|
|
245
|
+
if (nativeRaw) {
|
|
246
|
+
var nativeCtx = typeof nativeRaw === 'string' ? JSON.parse(nativeRaw) : nativeRaw
|
|
247
|
+
if (nativeCtx && nativeCtx.currentSequence) {
|
|
248
|
+
session.setInitialSequence(Number(nativeCtx.currentSequence))
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} catch (e) {}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 若当前 userId 由 Native 容器强行注入(跨端会话),H5 端的 Cookie/Storage 绝不覆盖
|
|
256
|
+
if (session.isNativeUserId && session.getUserId()) {
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
var adapter = tracker.getAdapter()
|
|
261
|
+
var uid = null
|
|
262
|
+
|
|
263
|
+
// 1. 跨端 NativeBridge 同步上下文优先(如果运行在 Android/iOS WebView 中)
|
|
264
|
+
if (typeof window !== 'undefined' && window.NativeBridge && typeof window.NativeBridge.getNativeContextSync === 'function') {
|
|
265
|
+
try {
|
|
266
|
+
var nativeRaw = window.NativeBridge.getNativeContextSync()
|
|
267
|
+
if (nativeRaw) {
|
|
268
|
+
var nativeCtx = typeof nativeRaw === 'string' ? JSON.parse(nativeRaw) : nativeRaw
|
|
269
|
+
if (nativeCtx && nativeCtx.userId) {
|
|
270
|
+
uid = String(nativeCtx.userId)
|
|
271
|
+
}
|
|
272
|
+
if (nativeCtx && nativeCtx.currentSequence) {
|
|
273
|
+
session.setInitialSequence(Number(nativeCtx.currentSequence))
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} catch (e) {}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// 2. URL query 跨端注入参数兜底(全面兼容标准 search 与 SPA Hash 路由后的 query)
|
|
280
|
+
if (typeof window !== 'undefined' && window.location) {
|
|
281
|
+
try {
|
|
282
|
+
var search = (window.location.search || '') + '&' + (window.location.hash || '')
|
|
283
|
+
var matchSeq = search.match(/[?&]_trk_seq=([^&]+)/)
|
|
284
|
+
if (matchSeq) {
|
|
285
|
+
session.setInitialSequence(Number(decodeURIComponent(matchSeq[1])))
|
|
286
|
+
}
|
|
287
|
+
if (!uid) {
|
|
288
|
+
var matchUid = search.match(/[?&]_trk_userId=([^&]+)/)
|
|
289
|
+
if (matchUid) {
|
|
290
|
+
uid = decodeURIComponent(matchUid[1])
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} catch (e) {}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 3. 存储单字段:userId / unionid / _tracker_user_id
|
|
297
|
+
if (!uid) {
|
|
298
|
+
uid = adapter.getStorage('userId')
|
|
299
|
+
|| adapter.getStorage('unionid')
|
|
300
|
+
|| adapter.getStorage('_tracker_user_id')
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 4. 读取 userInfo 复合对象
|
|
304
|
+
if (!uid) {
|
|
305
|
+
try {
|
|
306
|
+
var rawInfo = adapter.getStorage('userInfo')
|
|
307
|
+
if (rawInfo) {
|
|
308
|
+
var info = typeof rawInfo === 'string' ? JSON.parse(rawInfo) : rawInfo
|
|
309
|
+
if (info && typeof info === 'object') {
|
|
310
|
+
uid = info.userId || info.unionid || info.id || info.uid
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
} catch (e) {}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// 5. Cookie 兜底:rkfeb-userId 或 static-c-token (JWT payload)
|
|
317
|
+
if (!uid && typeof document !== 'undefined') {
|
|
318
|
+
try {
|
|
319
|
+
var matchCookieUid = document.cookie.match(/(?:^|;\s*)rkfeb-userId=([^;]+)/)
|
|
320
|
+
if (matchCookieUid) uid = decodeURIComponent(matchCookieUid[1])
|
|
321
|
+
|
|
322
|
+
if (!uid) {
|
|
323
|
+
var matchCookieToken = document.cookie.match(/(?:^|;\s*)(?:token|static-c-token|appToken)=([^;]+)/)
|
|
324
|
+
if (matchCookieToken) {
|
|
325
|
+
var payloadCookie = _parseJwtPayload(decodeURIComponent(matchCookieToken[1]))
|
|
326
|
+
if (payloadCookie) {
|
|
327
|
+
uid = payloadCookie.unionid || payloadCookie.userId || payloadCookie.uid || payloadCookie.sub
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
} catch (e) {}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 6. JWT Token 存储兜底:token / Token / appToken / rsc-token / rsb-token
|
|
335
|
+
if (!uid) {
|
|
336
|
+
var tokenKeys = ['token', 'Token', 'appToken', 'rsc-token', 'rsb-token']
|
|
337
|
+
for (var i = 0; i < tokenKeys.length; i++) {
|
|
338
|
+
var tokenVal = adapter.getStorage(tokenKeys[i])
|
|
339
|
+
if (tokenVal) {
|
|
340
|
+
var payload = _parseJwtPayload(tokenVal)
|
|
341
|
+
if (payload) {
|
|
342
|
+
uid = payload.unionid || payload.userId || payload.uid || payload.sub
|
|
343
|
+
if (uid) break
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 7. 防误置空核心保护:只有获取到有效非空 uid 才更新
|
|
350
|
+
var currentUid = session.getUserId()
|
|
351
|
+
if (uid && String(uid) !== String(currentUid)) {
|
|
352
|
+
tracker.setUser(String(uid))
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Vue.use 插件入口
|
|
358
|
+
*
|
|
359
|
+
* 用法:
|
|
360
|
+
* import TrackerSDK from './tracker-sdk/index.js'
|
|
361
|
+
* Vue.use(TrackerSDK, {
|
|
362
|
+
* appId: '1',
|
|
363
|
+
* dadianUrl: 'https://state.renruikeji.cn',
|
|
364
|
+
* platform: 'web',
|
|
365
|
+
* tokenKey: 'token',
|
|
366
|
+
* plugins: ['page-view', 'click', 'page-stay', 'js-error', 'app-lifecycle', 'api-watch', 'scroll-depth'],
|
|
367
|
+
* legacy: true,
|
|
368
|
+
* getAuthHeaders: () => ({ Authorization: orgToken, token: orgToken }),
|
|
369
|
+
* })
|
|
370
|
+
*/
|
|
371
|
+
function install(Vue, options) {
|
|
372
|
+
if (install._installed) return
|
|
373
|
+
install._installed = true
|
|
374
|
+
|
|
375
|
+
var tracker = createTracker(options || {})
|
|
376
|
+
|
|
377
|
+
// 0. 暴露 tracker 实例到 window(供原生容器及验证脚本访问)
|
|
378
|
+
if (typeof window !== 'undefined') {
|
|
379
|
+
window.__tracker = tracker
|
|
380
|
+
window.__tracker_pauseStay = function () { tracker.pausePageStay() }
|
|
381
|
+
window.__tracker_resumeStay = function () { tracker.resumePageStay() }
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// 0.1 设备注册(无 deviceId 时自动注册)
|
|
385
|
+
registerDeviceIfNeeded(tracker)
|
|
386
|
+
|
|
387
|
+
// 0.2 自动检测 userId(从 Cookie 或 storage 中读取已登录用户)
|
|
388
|
+
_ensureUserId(tracker)
|
|
389
|
+
|
|
390
|
+
// 0.3 注册 userId 刷新钩子:每次 flush 前从 storage 实时读取,
|
|
391
|
+
// 避免自动事件(page_view/click)在 userId 更新前用旧值发出
|
|
392
|
+
tracker.getQueue().setRefreshUserId(function () {
|
|
393
|
+
_ensureUserId(tracker)
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
// 1. 注册 $dadian(兼容现有所有业务调用)
|
|
397
|
+
var _rawDadian = createDadianProxy(tracker)
|
|
398
|
+
Vue.prototype.$dadian = function (frontUrl, postParam) {
|
|
399
|
+
_ensureUserId(tracker)
|
|
400
|
+
return _rawDadian(frontUrl, postParam)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// 2. 注册 $track(统一的上报方法,替代 $dadian)
|
|
404
|
+
// 行为与 $dadian 完全一致:第一个参数直接作为 frontUrl,不做任何路由拼接
|
|
405
|
+
Vue.prototype.$track = function (frontUrl, postParam) {
|
|
406
|
+
_ensureUserId(tracker)
|
|
407
|
+
|
|
408
|
+
// postParam 处理(与 $dadian 一致)
|
|
409
|
+
var postParamStr = ''
|
|
410
|
+
if (postParam && Object.keys(postParam).length > 0) {
|
|
411
|
+
var config = tracker.getConfig()
|
|
412
|
+
if (config.platform === 'weapp' && config.eventBus) {
|
|
413
|
+
var bus = config.eventBus
|
|
414
|
+
postParamStr = JSON.stringify(Object.assign({}, postParam, {
|
|
415
|
+
wxAppOnLaunch: bus.wxAppOnLaunch,
|
|
416
|
+
pageFrom: bus.pageFrom,
|
|
417
|
+
mallOrderExt: bus.mallOrderExt,
|
|
418
|
+
}))
|
|
419
|
+
} else {
|
|
420
|
+
postParamStr = JSON.stringify(postParam)
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// 开关关(默认):旧接口也报一次(与 $dadian 一致)
|
|
425
|
+
var config = tracker.getConfig()
|
|
426
|
+
if (!config.useUnifiedReport) {
|
|
427
|
+
var header = { 'content-type': 'application/json' }
|
|
428
|
+
var hasLegacyAuth = false
|
|
429
|
+
if (typeof config.getAuthHeaders === 'function') {
|
|
430
|
+
var authHeaders = config.getAuthHeaders() || {}
|
|
431
|
+
if (authHeaders.Authorization || authHeaders.token || authHeaders['rsb-token']) {
|
|
432
|
+
hasLegacyAuth = true
|
|
433
|
+
Object.assign(header, authHeaders)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (hasLegacyAuth) {
|
|
437
|
+
tracker.getQueue().pushLegacy(
|
|
438
|
+
config.dadianUrl + '/api/point/task/activeReport',
|
|
439
|
+
{ frontUrl: frontUrl, postParam: postParamStr },
|
|
440
|
+
header
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// SDK 事件入队(始终走)
|
|
446
|
+
tracker.track('custom', {
|
|
447
|
+
eventName: frontUrl || 'manual',
|
|
448
|
+
frontUrl: frontUrl,
|
|
449
|
+
postParam: postParamStr,
|
|
450
|
+
extraData: postParam,
|
|
451
|
+
elementId: frontUrl,
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
// 项目定制:从 footer 导航类 frontUrl 中提取目标路由,
|
|
455
|
+
// 在 track() 之后更新 referrer 链(确保目标页事件能拿到正确来源页)
|
|
456
|
+
// 必须在 track 之后:否则当前事件的 referrer 会被提前污染
|
|
457
|
+
var rules = config.projectRules
|
|
458
|
+
if (rules && typeof rules.extractNavTarget === 'function') {
|
|
459
|
+
var navTarget = rules.extractNavTarget(null, frontUrl)
|
|
460
|
+
if (navTarget) {
|
|
461
|
+
tracker.updatePageRoute(navTarget)
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// 3. 注册 $tracker 实例引用(含 resetSession 供登出时调用)
|
|
467
|
+
Vue.prototype.$tracker = tracker
|
|
468
|
+
Vue.prototype.$trackerResetSession = function () {
|
|
469
|
+
tracker.getSession().resetSession()
|
|
470
|
+
tracker.getQueue()._flushedEventIds.clear()
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// 3.1 动态参数解析器快捷方式
|
|
474
|
+
Vue.prototype.$registerParam = function (name, fn) {
|
|
475
|
+
tracker.registerParam(name, fn)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// 4. 注册 v-impression 指令(兼容旧业务代码)
|
|
479
|
+
var adapter = tracker.getAdapter()
|
|
480
|
+
if (adapter.supportsIntersectionObserver()) {
|
|
481
|
+
Vue.directive('impression', createImpressionDirective(tracker))
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// 5. 注册 v-track-exposure 指令(新的曝光指令,走自动采集链路)
|
|
485
|
+
var exposurePlugin = tracker.plugins.find(function (p) {
|
|
486
|
+
return p.name === 'exposure'
|
|
487
|
+
})
|
|
488
|
+
if (exposurePlugin && typeof exposurePlugin.createDirective === 'function') {
|
|
489
|
+
var trackExposureDirective = exposurePlugin.createDirective()
|
|
490
|
+
if (trackExposureDirective) {
|
|
491
|
+
Vue.directive('track-exposure', trackExposureDirective)
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// 6. 注册页面停留 mixin
|
|
496
|
+
var hasPageStayPlugin = (options.plugins || []).some(function (p) {
|
|
497
|
+
return p === 'page-stay' || (p && p.name === 'page-stay')
|
|
498
|
+
})
|
|
499
|
+
if (hasPageStayPlugin) {
|
|
500
|
+
Vue.mixin(createPageStayMixin(tracker))
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// 7. 注册 Vue 错误捕获(组件 render/lifecycle/watch 中的异常)
|
|
504
|
+
var jsErrorPlugin = tracker.plugins.find(function (p) {
|
|
505
|
+
return p.name === 'js-error'
|
|
506
|
+
})
|
|
507
|
+
if (jsErrorPlugin) {
|
|
508
|
+
var _prevErrorHandler = Vue.config.errorHandler
|
|
509
|
+
Vue.config.errorHandler = function (err, vm, info) {
|
|
510
|
+
jsErrorPlugin.handleVueError(err, vm, info)
|
|
511
|
+
if (_prevErrorHandler) _prevErrorHandler.call(this, err, vm, info)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// [PROJECT-ADAPT] 小程序生命周期注册
|
|
516
|
+
// H5 端通过 history API 监听路由变化,小程序端通过 onShow/onHide
|
|
517
|
+
if (tracker.getConfig().platform === 'weapp') {
|
|
518
|
+
var pageViewPlugin = tracker.plugins.find(function (p) {
|
|
519
|
+
return p.name === 'page-view'
|
|
520
|
+
})
|
|
521
|
+
var lifecyclePlugin = tracker.plugins.find(function (p) {
|
|
522
|
+
return p.name === 'app-lifecycle'
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
Vue.mixin({
|
|
526
|
+
onShow: function () {
|
|
527
|
+
if (pageViewPlugin) {
|
|
528
|
+
pageViewPlugin.trackPageShow()
|
|
529
|
+
}
|
|
530
|
+
if (lifecyclePlugin) {
|
|
531
|
+
lifecyclePlugin.trackAppShow()
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
onHide: function () {
|
|
535
|
+
if (lifecyclePlugin) {
|
|
536
|
+
lifecyclePlugin.trackAppHide()
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
})
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// 默认导出 Vue 插件对象
|
|
544
|
+
export default {
|
|
545
|
+
install: install,
|
|
546
|
+
createTracker: createTracker,
|
|
547
|
+
getTracker: getTracker,
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// 具名导出
|
|
551
|
+
export { Tracker } from './core/tracker.js'
|
|
552
|
+
export { WebAdapter } from './platforms/web.js'
|
|
553
|
+
export { WeappAdapter } from './platforms/weapp.js'
|
|
554
|
+
export { AppAdapter } from './platforms/app.js'
|