web-tracing-core 2.1.0

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.
Files changed (61) hide show
  1. package/__test__/css/performance.css +3 -0
  2. package/__test__/err-batch.spec.ts +47 -0
  3. package/__test__/err.spec.ts +82 -0
  4. package/__test__/event.spec.ts +62 -0
  5. package/__test__/html/performance.html +57 -0
  6. package/__test__/html/recordscreen.html +39 -0
  7. package/__test__/http.spec.ts +143 -0
  8. package/__test__/img/performance.png +0 -0
  9. package/__test__/js/performance.js +3 -0
  10. package/__test__/performance.spec.ts +112 -0
  11. package/__test__/recordscreen.spec.ts +50 -0
  12. package/__test__/utils/index.ts +99 -0
  13. package/__test__/utils/pollify.ts +14 -0
  14. package/__test__/utils.spec.ts +18 -0
  15. package/dist/LICENSE +21 -0
  16. package/dist/README.md +97 -0
  17. package/dist/index.cjs +15943 -0
  18. package/dist/index.d.ts +323 -0
  19. package/dist/index.iife.js +15946 -0
  20. package/dist/index.iife.min.js +28 -0
  21. package/dist/index.mjs +15913 -0
  22. package/dist/package.json +49 -0
  23. package/index.ts +75 -0
  24. package/package.json +49 -0
  25. package/src/common/config.ts +13 -0
  26. package/src/common/constant.ts +57 -0
  27. package/src/common/index.ts +2 -0
  28. package/src/lib/base.ts +129 -0
  29. package/src/lib/err-batch.ts +134 -0
  30. package/src/lib/err.ts +323 -0
  31. package/src/lib/event-dwell.ts +63 -0
  32. package/src/lib/event.ts +252 -0
  33. package/src/lib/eventBus.ts +97 -0
  34. package/src/lib/exportMethods.ts +208 -0
  35. package/src/lib/http.ts +197 -0
  36. package/src/lib/intersectionObserver.ts +164 -0
  37. package/src/lib/line-status.ts +45 -0
  38. package/src/lib/options.ts +325 -0
  39. package/src/lib/performance.ts +302 -0
  40. package/src/lib/pv.ts +199 -0
  41. package/src/lib/recordscreen.ts +169 -0
  42. package/src/lib/replace.ts +371 -0
  43. package/src/lib/sendData.ts +264 -0
  44. package/src/observer/computed.ts +52 -0
  45. package/src/observer/config.ts +1 -0
  46. package/src/observer/dep.ts +21 -0
  47. package/src/observer/index.ts +91 -0
  48. package/src/observer/ref.ts +80 -0
  49. package/src/observer/types.ts +22 -0
  50. package/src/observer/watch.ts +19 -0
  51. package/src/observer/watcher.ts +88 -0
  52. package/src/types/index.ts +126 -0
  53. package/src/utils/debug.ts +17 -0
  54. package/src/utils/element.ts +47 -0
  55. package/src/utils/fingerprintjs.ts +2132 -0
  56. package/src/utils/getIps.ts +127 -0
  57. package/src/utils/global.ts +49 -0
  58. package/src/utils/index.ts +551 -0
  59. package/src/utils/is.ts +78 -0
  60. package/src/utils/localStorage.ts +70 -0
  61. package/src/utils/session.ts +27 -0
@@ -0,0 +1,97 @@
1
+ import type { AnyFun } from '../types'
2
+ import { EVENTTYPES } from '../common'
3
+ import { _support } from '../utils/global'
4
+
5
+ interface EventHandler {
6
+ type: EVENTTYPES
7
+ callback: AnyFun
8
+ }
9
+
10
+ type Handlers = {
11
+ [key in EVENTTYPES]?: AnyFun[]
12
+ }
13
+
14
+ export class EventBus {
15
+ private handlers: Handlers
16
+ constructor() {
17
+ this.handlers = {}
18
+ }
19
+ /**
20
+ * 为目标类型事件添加回调
21
+ * @param handler 需要被添加的类型以及回调函数
22
+ */
23
+ addEvent(handler: EventHandler) {
24
+ !this.handlers[handler.type] && (this.handlers[handler.type] = [])
25
+ const funIndex = this._getCallbackIndex(handler)
26
+ if (funIndex === -1) {
27
+ this.handlers[handler.type]?.push(handler.callback)
28
+ }
29
+ }
30
+ /**
31
+ * 为目标类型事件删除回调
32
+ * @param handler 需要被删除的类型以及回调函数
33
+ */
34
+ delEvent(handler: EventHandler) {
35
+ const funIndex = this._getCallbackIndex(handler)
36
+ if (funIndex !== -1) {
37
+ this.handlers[handler.type]?.splice(funIndex, 1)
38
+ }
39
+ }
40
+ /**
41
+ * 为目标类型事件更改回调
42
+ * @param handler 需要被更改的类型以及回调函数
43
+ * @param newCallback 新的回调函数
44
+ */
45
+ changeEvent(handler: EventHandler, newCallback: AnyFun) {
46
+ const funIndex = this._getCallbackIndex(handler)
47
+ if (funIndex !== -1) {
48
+ this.handlers[handler.type]?.splice(funIndex, 1, newCallback)
49
+ }
50
+ }
51
+ /**
52
+ * 获取目标类型事件所有的回调
53
+ * @param type 事件类型
54
+ */
55
+ getEvent(type: EVENTTYPES): AnyFun[] {
56
+ return this.handlers[type] || []
57
+ }
58
+ /**
59
+ * 执行目标类型事件所有的回调
60
+ * @param type 事件类型
61
+ * @param args 额外参数
62
+ */
63
+ runEvent(type: EVENTTYPES, ...args: any[]): void {
64
+ const allEvent = this.getEvent(type)
65
+ allEvent.forEach(fun => {
66
+ fun(...args)
67
+ })
68
+ }
69
+ /**
70
+ * 获取函数在 callback 列表中的位置
71
+ */
72
+ private _getCallbackIndex(handler: EventHandler): number {
73
+ if (this.handlers[handler.type]) {
74
+ const callbackList = this.handlers[handler.type]
75
+ if (callbackList) {
76
+ return callbackList.findIndex(fun => fun === handler.callback)
77
+ } else {
78
+ return -1
79
+ }
80
+ } else {
81
+ return -1
82
+ }
83
+ }
84
+ /**
85
+ * 移除多个指定类型的事件监听
86
+ * @param types 事件类型数组
87
+ */
88
+ removeEvents(types: EVENTTYPES[]) {
89
+ types.forEach(type => {
90
+ delete this.handlers[type]
91
+ })
92
+ }
93
+ }
94
+
95
+ const eventBus = _support.eventBus || (_support.eventBus = new EventBus())
96
+
97
+ export { eventBus }
@@ -0,0 +1,208 @@
1
+ import type {
2
+ InternalOptions,
3
+ AnyFun,
4
+ TargetGather,
5
+ ElementOrList,
6
+ VoidFun
7
+ } from '../types'
8
+ import { options } from './options'
9
+ import { _support } from '../utils/global'
10
+ import { getIPs as _getIPs } from '../utils/getIps'
11
+ import { validateMethods, deepCopy } from '../utils'
12
+ import { handleSendError } from './err'
13
+ import { handleSendPerformance } from './performance'
14
+ import { handleSendEvent } from './event'
15
+ import { handleSendPageView } from './pv'
16
+ import { intersection } from './intersectionObserver'
17
+ import { sendData } from './sendData'
18
+ import { SDK_LOCAL_KEY } from '../common/config'
19
+ import { LocalStorageUtil } from '../utils/localStorage'
20
+ import { unzip } from '../lib/recordscreen'
21
+
22
+ /**
23
+ * 解压错误录屏数据
24
+ */
25
+ export function unzipRecordscreen(recordscreen: string) {
26
+ return unzip(recordscreen)
27
+ }
28
+
29
+ /**
30
+ * 钩子:放入事件队列之前
31
+ * @param fun 回调函数
32
+ */
33
+ export function beforePushEventList(fun: AnyFun): void {
34
+ if (!validateMethods('beforePushEventList')) return
35
+
36
+ options.value.beforePushEventList.push(fun)
37
+ }
38
+
39
+ /**
40
+ * 钩子:发送之前
41
+ * @param fun 回调函数
42
+ */
43
+ export function beforeSendData(fun: AnyFun): void {
44
+ if (!validateMethods('beforeSendData')) return
45
+
46
+ options.value.beforeSendData.push(fun)
47
+ }
48
+
49
+ /**
50
+ * 钩子:发送之后
51
+ * @param fun 回调函数
52
+ */
53
+ export function afterSendData(fun: AnyFun): void {
54
+ if (!validateMethods('afterSendData')) return
55
+
56
+ options.value.afterSendData.push(fun)
57
+ }
58
+
59
+ /**
60
+ * 设置用户id
61
+ * @param id 用户id
62
+ */
63
+ export function setUserUuid(id: string): void {
64
+ if (!validateMethods('setUserUuid')) return
65
+
66
+ options.value.userUuid = id
67
+ }
68
+
69
+ /**
70
+ * 获取用户id(此id是手动设置的id)
71
+ */
72
+ export function getUserUuid(): string | void {
73
+ if (!validateMethods('getUserUuid')) return
74
+
75
+ return options.value.userUuid
76
+ }
77
+
78
+ /**
79
+ * 获取sdk中的用户id
80
+ */
81
+ export function getSDKUserUuid(): string | void {
82
+ if (!validateMethods('getSDKUserUuid')) return
83
+
84
+ return options.value.sdkUserUuid
85
+ }
86
+
87
+ /**
88
+ * 获取在sdk中记录的所有基础的信息(包括硬件,地理位置等等)
89
+ */
90
+ export function getBaseInfo(): object | void {
91
+ if (!validateMethods('getBaseInfo')) return
92
+
93
+ return {
94
+ ..._support.baseInfo.base,
95
+ userUuid: options.value.userUuid
96
+ }
97
+ }
98
+
99
+ /**
100
+ * 获取首屏数据
101
+ */
102
+ export function getFirstScreen(): object | void {
103
+ if (!validateMethods('getFirstScreen')) return
104
+
105
+ return { ..._support.firstScreen }
106
+ }
107
+
108
+ /**
109
+ * 主动触发error事件上报
110
+ * @param options 自定义配置信息
111
+ */
112
+ export function traceError(options = {}, flush = false) {
113
+ if (!validateMethods('traceError')) return
114
+
115
+ return handleSendError(options, flush)
116
+ }
117
+
118
+ /**
119
+ * 主动触发性能事件上报
120
+ * @param options 自定义配置信息
121
+ */
122
+ export function tracePerformance(options = {}, flush = false) {
123
+ if (!validateMethods('tracePerformance')) return
124
+
125
+ return handleSendPerformance(options, flush)
126
+ }
127
+
128
+ /**
129
+ * 主动触发事件上报
130
+ * @param options 自定义配置信息
131
+ */
132
+ export function traceCustomEvent(options = {}, flush = false) {
133
+ if (!validateMethods('traceCustomEvent')) return
134
+
135
+ return handleSendEvent(options, flush)
136
+ }
137
+
138
+ /**
139
+ * 主动触发pv事件上报
140
+ * @param options 自定义配置信息
141
+ */
142
+ export function tracePageView(option = {}, flush = false) {
143
+ if (!validateMethods('tracePageView')) return
144
+
145
+ return handleSendPageView(option, flush)
146
+ }
147
+
148
+ /**
149
+ * 获取公网ip
150
+ */
151
+ export function getIPs(): Promise<string> {
152
+ return _getIPs().then((res: any) => res[0])
153
+ }
154
+
155
+ /**
156
+ * 曝光 - 对目标元素进行监听
157
+ * @param params 附带的额外参数
158
+ */
159
+ export function intersectionObserver(gather: TargetGather): void {
160
+ if (!validateMethods('intersectionObserver')) return
161
+ intersection.observe(gather)
162
+ }
163
+
164
+ /**
165
+ * 曝光 - 对目标元素进行取消监听
166
+ */
167
+ export function intersectionUnobserve(target: ElementOrList): void {
168
+ if (!validateMethods('intersectionUnobserve')) return
169
+ intersection.unobserve(target)
170
+ }
171
+
172
+ /**
173
+ * 曝光 - 取消所有监听
174
+ */
175
+ export function intersectionDisconnect(): void {
176
+ if (!validateMethods('intersectionDisconnect')) return
177
+ intersection.disconnect()
178
+ }
179
+
180
+ /**
181
+ * 手动发送本地数据
182
+ */
183
+ export function sendLocal(): void {
184
+ if (!validateMethods('sendData') && !options.value.localization) return
185
+ const localItem = LocalStorageUtil.getItem(SDK_LOCAL_KEY)
186
+ if (localItem) {
187
+ sendData.sendLocal(localItem)
188
+ LocalStorageUtil.removeItem(SDK_LOCAL_KEY)
189
+ }
190
+ }
191
+
192
+ /**
193
+ * 设置本地化存储溢出后的回调
194
+ * @param overFlowFun 回调函数
195
+ */
196
+ export function setLocalizationOverFlow(overFlowFun: VoidFun): void {
197
+ if (!validateMethods('localizationOverFlow') && !options.value.localization)
198
+ return
199
+ options.value.localizationOverFlow = overFlowFun
200
+ }
201
+
202
+ /**
203
+ * 获取sdk内部的参数配置
204
+ * 这个参数配置并不是入参配置,sdk内部的参数是整理过后的
205
+ */
206
+ export function getOptions(): InternalOptions {
207
+ return deepCopy(options.value)
208
+ }
@@ -0,0 +1,197 @@
1
+ import {
2
+ on,
3
+ isValidKey,
4
+ getTimestamp,
5
+ parseGetParams,
6
+ getBaseUrl
7
+ } from '../utils'
8
+ import { handleSendError } from './err'
9
+ import { eventBus } from './eventBus'
10
+ import { EVENTTYPES, SENDID } from '../common'
11
+ import { options } from './options'
12
+ import { handleSendPerformance } from './performance'
13
+ // import { debug } from '../utils/debug'
14
+ import { isRegExp } from '../utils/is'
15
+
16
+ /**
17
+ * fetch请求拦截
18
+ */
19
+ function interceptFetch(): void {
20
+ eventBus.addEvent({
21
+ type: EVENTTYPES.FETCH,
22
+ callback: async (
23
+ reqUrl: string,
24
+ _options: Partial<Request> = {},
25
+ res: Response,
26
+ fetchStart: number,
27
+ traceObj: Partial<Request> = {}
28
+ ) => {
29
+ const { method = 'GET', body = {} } = _options
30
+ const { url, status, statusText } = res
31
+ const requestMethod = String(method).toLocaleLowerCase()
32
+
33
+ if (isIgnoreHttp(url)) return
34
+
35
+ if (status === 200 || status === 304) {
36
+ if (options.value.performance.server) {
37
+ handleSendPerformance({
38
+ eventId: SENDID.SERVER,
39
+ requestUrl: url,
40
+ triggerTime: fetchStart,
41
+ duration: getTimestamp() - fetchStart,
42
+ responseStatus: status,
43
+ requestMethod,
44
+ requestType: 'fetch',
45
+ ...traceObj,
46
+ params: method.toUpperCase() === 'POST' ? body : parseGetParams(url)
47
+ })
48
+ }
49
+ } else if (options.value.error.server) {
50
+ handleSendError({
51
+ eventId: SENDID.SERVER,
52
+ triggerTime: fetchStart,
53
+ duration: getTimestamp() - fetchStart,
54
+ errMessage: statusText,
55
+ requestUrl: getBaseUrl(url),
56
+ responseStatus: status,
57
+ requestMethod,
58
+ requestType: 'fetch',
59
+ ...traceObj,
60
+ params: method.toUpperCase() === 'POST' ? body : parseGetParams(url)
61
+ })
62
+ }
63
+ }
64
+ })
65
+ }
66
+
67
+ class RequestTemplate {
68
+ requestUrl = '' // 请求地址
69
+ requestMethod = '' // 请求类型 GET POST
70
+ requestParams = {} // get请求的参数
71
+ triggerTime = -1 // 请求发生时间
72
+ constructor(config = {}) {
73
+ Object.keys(config).forEach(key => {
74
+ if (isValidKey(key, config)) {
75
+ this[key] = config[key] || null
76
+ }
77
+ })
78
+ }
79
+ }
80
+
81
+ /**
82
+ * xhr 请求拦截
83
+ */
84
+ function interceptXHR(): void {
85
+ const _config = new RequestTemplate()
86
+
87
+ eventBus.addEvent({
88
+ type: EVENTTYPES.XHROPEN,
89
+ callback: (method, url) => {
90
+ _config.requestMethod = String(method).toLocaleLowerCase()
91
+ _config.requestUrl = url
92
+ _config.requestParams = parseGetParams(url)
93
+ }
94
+ })
95
+
96
+ eventBus.addEvent({
97
+ type: EVENTTYPES.XHRSEND,
98
+ callback: (that: XMLHttpRequest & any, body) => {
99
+ // readyState发生改变时触发,也就是请求状态改变时
100
+ // readyState 会依次变为 2,3,4 也就是会触发三次这里
101
+ on(that, EVENTTYPES.READYSTATECHANGE, function () {
102
+ const { readyState, status, responseURL, statusText } = that
103
+ const responseText =
104
+ that.responseType === '' || that.responseType === 'text'
105
+ ? that.responseText
106
+ : ''
107
+
108
+ if (readyState === 4) {
109
+ const requestUrl = responseURL || _config.requestUrl
110
+ if (isIgnoreHttp(requestUrl)) return
111
+
112
+ // 请求已完成,且响应已就绪
113
+ if (status === 200 || status === 304) {
114
+ if (options.value.performance.server) {
115
+ handleSendPerformance({
116
+ eventId: SENDID.SERVER,
117
+ requestUrl,
118
+ requestMethod: _config.requestMethod,
119
+ requestType: 'xhr',
120
+ responseStatus: status,
121
+ duration: getTimestamp() - _config.triggerTime,
122
+ params:
123
+ _config.requestMethod === 'post'
124
+ ? body
125
+ : _config.requestParams
126
+ })
127
+ }
128
+ } else if (options.value.error.server) {
129
+ handleSendError({
130
+ eventId: SENDID.SERVER,
131
+ errMessage: statusText || responseText,
132
+ requestUrl,
133
+ requestMethod: _config.requestMethod,
134
+ requestType: 'xhr',
135
+ responseStatus: status,
136
+ params:
137
+ _config.requestMethod === 'post' ? body : _config.requestParams
138
+ })
139
+ }
140
+ }
141
+ })
142
+
143
+ _config.triggerTime = getTimestamp()
144
+ }
145
+ })
146
+ }
147
+
148
+ /**
149
+ * 判断请求地址是否为需要拦截的
150
+ * @param url 请求地址
151
+ */
152
+ function isIgnoreHttp(url: string): boolean {
153
+ if (!options.value.ignoreRequest.length) return false
154
+ if (!url) return false
155
+
156
+ return options.value.ignoreRequest.some(item => {
157
+ if (isRegExp(item)) {
158
+ if ((item as RegExp).test(url)) {
159
+ // debug(`ignoreRequest拦截成功 - 截条件:${item} 拦截地址:${url}`)
160
+ return true
161
+ } else {
162
+ return false
163
+ }
164
+ } else {
165
+ if (url === item) {
166
+ // debug(`ignoreRequest拦截成功 - 截条件:${item} 拦截地址:${url}`)
167
+ return true
168
+ } else {
169
+ return false
170
+ }
171
+ }
172
+ })
173
+ }
174
+
175
+ /**
176
+ * 初始化http监控
177
+ */
178
+ function initHttp(): void {
179
+ if (!options.value.performance.server && !options.value.error.server) return
180
+
181
+ interceptXHR()
182
+ interceptFetch()
183
+ }
184
+
185
+ /**
186
+ * 卸载所有请求监听
187
+ */
188
+ export function destroyHttp() {
189
+ // 清除HTTP请求相关的事件类型
190
+ eventBus.removeEvents([
191
+ EVENTTYPES.FETCH,
192
+ EVENTTYPES.XHROPEN,
193
+ EVENTTYPES.XHRSEND
194
+ ])
195
+ }
196
+
197
+ export { initHttp }
@@ -0,0 +1,164 @@
1
+ import type { ElementOrList, TargetGather, AnyObj } from '../types'
2
+ import { unKnowToArray, getTimestamp, getLocationHref } from '../utils'
3
+ import { sendData } from './sendData'
4
+ import { _support } from '../utils/global'
5
+ import { SEDNEVENTTYPES } from '../common'
6
+
7
+ interface IoMap {
8
+ [key: number]: IntersectionObserver
9
+ }
10
+
11
+ interface TargetMap {
12
+ target: Element
13
+ threshold: number
14
+ observeTime: number // sdk开始监视的时间
15
+ showTime?: number // sdk检测到的开始时间
16
+ showEndTime?: number // sdk检测到的结束时间
17
+ params?: AnyObj
18
+ }
19
+
20
+ /**
21
+ * 元素曝光收集
22
+ * 收集参数:曝光开始时间、曝光结束时间、被曝光元素上附带的额外参数
23
+ * 收集契机:划出目标元素的收集范围
24
+ */
25
+ class Intersection {
26
+ private ioMap: IoMap = {}
27
+ private targetMap: TargetMap[] = []
28
+ private options = {
29
+ root: null,
30
+ rootMargin: '0px',
31
+ threshold: 0.5 // 阀值设为0.5,当只有比例达到一半时才触发回调函数
32
+ }
33
+ /**
34
+ * 针对 threshold 生成不同监听对象 (不允许同一个dom被两个监听对象监听)
35
+ * @param threshold 阈值
36
+ */
37
+ private initObserver(threshold: number) {
38
+ return new IntersectionObserver(
39
+ entries => {
40
+ entries.forEach(entry => {
41
+ if (entry.isIntersecting) {
42
+ const targetObj = this.targetMap.find(
43
+ mapTarget => mapTarget.target === entry.target
44
+ )
45
+ if (targetObj) {
46
+ targetObj.showTime = getTimestamp()
47
+ }
48
+ } else {
49
+ const targetObj = this.targetMap.find(
50
+ mapTarget => mapTarget.target === entry.target
51
+ )
52
+ if (targetObj) {
53
+ // 在进入页面时指定了没有在屏幕可视界面的dom,会立即触发这里
54
+ // 此时需要根据有无 showTime 区分是否为一个完整事件再去发送
55
+ if (!targetObj.showTime) return
56
+ targetObj.showEndTime = getTimestamp()
57
+ this.sendEvent(targetObj)
58
+ }
59
+ }
60
+ })
61
+ },
62
+ {
63
+ ...this.options,
64
+ threshold
65
+ }
66
+ )
67
+ }
68
+ /**
69
+ * 发送事件
70
+ */
71
+ private sendEvent(targetObj: TargetMap) {
72
+ sendData.emit({
73
+ eventType: SEDNEVENTTYPES.INTERSECTION,
74
+ triggerPageUrl: getLocationHref(),
75
+ ...targetObj
76
+ })
77
+ }
78
+ /**
79
+ * 开始观察目标元素
80
+ * 分为初始加载和过程中加载
81
+ * @param params 附带的额外参数
82
+ */
83
+ public observe(gather: TargetGather | TargetGather[]) {
84
+ const _gather = unKnowToArray(gather)
85
+ _gather.forEach(item => {
86
+ const _targetList = unKnowToArray(item.target)
87
+
88
+ if (!Object.prototype.hasOwnProperty.call(this.ioMap, item.threshold)) {
89
+ this.ioMap[item.threshold] = this.initObserver(item.threshold || 0.5)
90
+ }
91
+
92
+ _targetList.forEach(target => {
93
+ const index = this.targetMap.findIndex(
94
+ mapTarget => mapTarget.target === target
95
+ )
96
+ // 不允许重复观察
97
+ if (index === -1) {
98
+ this.ioMap[item.threshold].observe(target)
99
+
100
+ // 记录哪些元素被监听
101
+ this.targetMap.push({
102
+ target,
103
+ threshold: item.threshold,
104
+ observeTime: getTimestamp(), // 开始监听的时间
105
+ params: item.params
106
+ })
107
+ }
108
+ })
109
+ })
110
+ }
111
+ /**
112
+ * 对元素停止观察
113
+ */
114
+ public unobserve(target: ElementOrList) {
115
+ const _targetList = unKnowToArray(target)
116
+
117
+ _targetList.forEach(_target => {
118
+ // 第一步:找出此元素代表的 threshold 值
119
+ const targetIndex = this.targetMap.findIndex(
120
+ mapTarget => mapTarget.target === _target
121
+ )
122
+ if (targetIndex === -1) return // 不存在的元素则跳过
123
+
124
+ // 第二步:根据 threshold 值从 ioMap 获取到 io 实例
125
+ const io = this.ioMap[this.targetMap[targetIndex].threshold]
126
+ if (!io) return
127
+
128
+ this.targetMap.splice(targetIndex, 1)
129
+
130
+ // 第二步:io 实例执行 unobserve 方法
131
+ io.unobserve(_target)
132
+ })
133
+ }
134
+ /**
135
+ * 对所有元素停止观察
136
+ */
137
+ public disconnect() {
138
+ for (const key in this.ioMap) {
139
+ if (Object.prototype.hasOwnProperty.call(this.ioMap, key)) {
140
+ const io = this.ioMap[key]
141
+ io.disconnect()
142
+ }
143
+ }
144
+ this.targetMap = []
145
+ this.ioMap = {}
146
+ }
147
+ }
148
+
149
+ export let intersection: Intersection
150
+
151
+ /**
152
+ * 初始化曝光监听
153
+ */
154
+ export function initIntersection() {
155
+ _support.intersection = new Intersection()
156
+ intersection = _support.intersection
157
+ }
158
+
159
+ /**
160
+ * 卸载所有曝光监听
161
+ */
162
+ export function destroyIntersection() {
163
+ intersection?.disconnect()
164
+ }
@@ -0,0 +1,45 @@
1
+ import { _support } from '../utils/global'
2
+ import { EVENTTYPES } from '../common'
3
+ import { eventBus } from './eventBus'
4
+ import { debug } from '../utils/debug'
5
+
6
+ /**
7
+ * 监听网络状态
8
+ * 当处于断网状态下的所有埋点事件都无效(认为此时采集的数据大部分是无效的)
9
+ */
10
+ export class LineStatus {
11
+ onLine = true
12
+ constructor() {
13
+ this.init()
14
+ }
15
+ init() {
16
+ eventBus.addEvent({
17
+ type: EVENTTYPES.OFFLINE,
18
+ callback: e => {
19
+ if (e.type === 'offline') {
20
+ debug('网络断开')
21
+ this.onLine = false
22
+ }
23
+ }
24
+ })
25
+ eventBus.addEvent({
26
+ type: EVENTTYPES.ONLINE,
27
+ callback: e => {
28
+ if (e.type === 'online') {
29
+ debug('网络连接')
30
+ this.onLine = true
31
+ }
32
+ }
33
+ })
34
+ }
35
+ }
36
+
37
+ export let lineStatus: LineStatus
38
+
39
+ /**
40
+ * 初始化网络监听
41
+ */
42
+ export function initLineStatus() {
43
+ _support.lineStatus = new LineStatus()
44
+ lineStatus = _support.lineStatus
45
+ }