monitor-track 1.10.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 (65) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +101 -0
  3. package/dist/CHANGELOG.md +21 -0
  4. package/dist/README.md +101 -0
  5. package/dist/cjs/config/global.d.ts +17 -0
  6. package/dist/cjs/config/index.d.ts +33 -0
  7. package/dist/cjs/constant.d.ts +2 -0
  8. package/dist/cjs/handlers/error.d.ts +23 -0
  9. package/dist/cjs/handlers/index.d.ts +3 -0
  10. package/dist/cjs/handlers/pv.d.ts +24 -0
  11. package/dist/cjs/handlers/user-activity.d.ts +9 -0
  12. package/dist/cjs/index.d.ts +30 -0
  13. package/dist/cjs/index.js +1485 -0
  14. package/dist/cjs/main.d.ts +1 -0
  15. package/dist/cjs/reporter.d.ts +198 -0
  16. package/dist/cjs/types/config.d.ts +59 -0
  17. package/dist/cjs/types/global.d.ts +70 -0
  18. package/dist/cjs/types/index.d.ts +189 -0
  19. package/dist/cjs/utils/index.d.ts +45 -0
  20. package/dist/esm/_virtual/_tslib.js +26 -0
  21. package/dist/esm/config/global.d.ts +17 -0
  22. package/dist/esm/config/global.js +271 -0
  23. package/dist/esm/config/index.d.ts +33 -0
  24. package/dist/esm/config/index.js +32 -0
  25. package/dist/esm/constant.d.ts +2 -0
  26. package/dist/esm/constant.js +4 -0
  27. package/dist/esm/handlers/error.d.ts +23 -0
  28. package/dist/esm/handlers/error.js +181 -0
  29. package/dist/esm/handlers/index.d.ts +3 -0
  30. package/dist/esm/handlers/pv.d.ts +24 -0
  31. package/dist/esm/handlers/pv.js +99 -0
  32. package/dist/esm/handlers/user-activity.d.ts +9 -0
  33. package/dist/esm/handlers/user-activity.js +66 -0
  34. package/dist/esm/index.d.ts +30 -0
  35. package/dist/esm/index.js +115 -0
  36. package/dist/esm/main.d.ts +1 -0
  37. package/dist/esm/package.json.js +3 -0
  38. package/dist/esm/reporter.d.ts +198 -0
  39. package/dist/esm/reporter.js +359 -0
  40. package/dist/esm/types/config.d.ts +59 -0
  41. package/dist/esm/types/global.d.ts +70 -0
  42. package/dist/esm/types/index.d.ts +189 -0
  43. package/dist/esm/utils/index.d.ts +45 -0
  44. package/dist/esm/utils/index.js +354 -0
  45. package/dist/index.js +1534 -0
  46. package/dist/package.json +41 -0
  47. package/package.json +42 -0
  48. package/rollup.config.js +66 -0
  49. package/scripts/cp.js +11 -0
  50. package/src/config/global.ts +309 -0
  51. package/src/config/index.ts +53 -0
  52. package/src/constant.ts +2 -0
  53. package/src/handlers/error.ts +187 -0
  54. package/src/handlers/index.ts +3 -0
  55. package/src/handlers/pv.ts +124 -0
  56. package/src/handlers/user-activity.ts +69 -0
  57. package/src/index.ts +127 -0
  58. package/src/main.ts +8 -0
  59. package/src/reporter.ts +372 -0
  60. package/src/types/config.ts +58 -0
  61. package/src/types/global.ts +73 -0
  62. package/src/types/index.ts +227 -0
  63. package/src/typing.d.ts +15 -0
  64. package/src/utils/index.ts +371 -0
  65. package/tsconfig.esm.json +14 -0
@@ -0,0 +1,271 @@
1
+ import { v4 } from 'uuid';
2
+ import { report } from '../reporter.js';
3
+ import { getSessionId, getNavigator, getViewport, getUid } from '../utils/index.js';
4
+ import { setRouteStack, setPVTime } from '../handlers/pv.js';
5
+ import { Config } from './index.js';
6
+ import { version } from '../package.json.js';
7
+
8
+ /*
9
+ * @Author: Mark.Zhang
10
+ * @Date: 2020-10-26 15:40:16
11
+ * @Description 全局配置项
12
+ */
13
+ // 上报数据
14
+ let Report = {
15
+ uuid: '',
16
+ projectID: '',
17
+ host: '',
18
+ originPage: '',
19
+ page: '',
20
+ time: 0,
21
+ browserName: '',
22
+ browserVersion: '',
23
+ engineName: '',
24
+ engineVersion: '',
25
+ language: '',
26
+ navigatorVendor: '',
27
+ connectionType: '2g',
28
+ osName: '',
29
+ osVersion: '',
30
+ type: 'init',
31
+ viewport: '',
32
+ screen: '',
33
+ version: '',
34
+ charset: '',
35
+ pageTitle: '',
36
+ referrer: '',
37
+ pv: null,
38
+ ua: null,
39
+ error: null,
40
+ dpr: 1,
41
+ perf: null,
42
+ vD: undefined,
43
+ manualReport: undefined,
44
+ lifeCycleId: v4(),
45
+ sessionId: getSessionId(),
46
+ };
47
+ /**
48
+ * @description 初始化上报数据并上报
49
+ */
50
+ function initReport() {
51
+ if (document.readyState === 'complete') {
52
+ initReportFunc();
53
+ }
54
+ else {
55
+ // 注意:这里不要使用window.onload,因为一个项目window.onload只能用一次,如果这里用了就会影响宿主项目的功能
56
+ window.addEventListener('load', initReportFunc);
57
+ }
58
+ }
59
+ function initReportFunc() {
60
+ setRouteStack([location.href, location.href]);
61
+ setPVTime();
62
+ Report = Object.assign(Object.assign({}, getReport()), {
63
+ page: location.href,
64
+ originPage: location.href,
65
+ type: 'init',
66
+ perf: performance,
67
+ sessionId: getSessionId(),
68
+ lifeCycleId: v4(),
69
+ });
70
+ report(Report);
71
+ }
72
+ /**
73
+ * @description 设置Report的值
74
+ * @param key Report key
75
+ * @param value Report 值
76
+ */
77
+ function setReportValue(key, value) {
78
+ if (Object.prototype.hasOwnProperty.call(Report, key)) {
79
+ if (['pv', 'ua', 'error', 'request'].includes(key)) {
80
+ Report = Object.assign(Object.assign({}, Report), {
81
+ pv: null,
82
+ ua: null,
83
+ error: null,
84
+ req: null,
85
+ manualReport: undefined,
86
+ perf: null,
87
+ });
88
+ }
89
+ Report[key] = value;
90
+ }
91
+ }
92
+ /**
93
+ * @description 获取上报数据
94
+ */
95
+ function getReport() {
96
+ const nav = getNavigator();
97
+ const viewport = getViewport();
98
+ return Object.assign(Object.assign(Object.assign({}, Report), nav), {
99
+ version,
100
+ projectID: Config.projectID,
101
+ host: location.host,
102
+ uuid: getUid(),
103
+ viewport,
104
+ screen: `${screen.width} x ${screen.height}`,
105
+ pageTitle: document.title,
106
+ referrer: document.referrer,
107
+ charset: document.charset,
108
+ customPayload: Config.customPayload,
109
+ dpr: window.devicePixelRatio,
110
+ });
111
+ }
112
+ function ajaxEventTrigger(event) {
113
+ const ajaxEvent = new CustomEvent(event, {
114
+ detail: this,
115
+ });
116
+ window.dispatchEvent(ajaxEvent);
117
+ }
118
+ const OldXHR = window.XMLHttpRequest;
119
+ function newXHR() {
120
+ const realXHR = new OldXHR();
121
+ realXHR.addEventListener('loadstart', function () {
122
+ ajaxEventTrigger.call(this, 'ajaxLoadStart');
123
+ }, false);
124
+ realXHR.addEventListener('loadend', function () {
125
+ ajaxEventTrigger.call(this, 'ajaxLoadEnd');
126
+ }, false);
127
+ // 此处的捕获的异常会连日志接口也一起捕获,如果日志上报接口异常了,就会导致死循环了。
128
+ realXHR.onerror = function (e) {
129
+ // eslint-disable-next-line no-console
130
+ console.warn('realXHR.onerror, e', e);
131
+ };
132
+ return realXHR;
133
+ }
134
+ /**
135
+ * 页面接口请求监控
136
+ */
137
+ const tempUrlInfo = {};
138
+ const recordXMLHttpRequestLog = (XMLHttpRequestTimeout) => {
139
+ XMLHttpRequestTimeout = typeof XMLHttpRequestTimeout === 'number' ? XMLHttpRequestTimeout : 1000;
140
+ const timeRecordArray = [];
141
+ window.__XMLHttpRequest__ = window.XMLHttpRequest;
142
+ window.XMLHttpRequest = newXHR;
143
+ window.addEventListener('ajaxLoadStart', function (e) {
144
+ const tempObj = {
145
+ timeStamp: new Date().getTime(),
146
+ event: e,
147
+ };
148
+ timeRecordArray.push(tempObj);
149
+ });
150
+ window.addEventListener('ajaxLoadEnd', function () {
151
+ const timeRecordArrayCopy = [].concat(timeRecordArray);
152
+ for (let i = 0; i < timeRecordArrayCopy.length; i++) {
153
+ if (timeRecordArrayCopy[i].event.detail && timeRecordArrayCopy[i].event.detail.status > 0) {
154
+ const currentTime = new Date().getTime();
155
+ const { responseURL, status, statusText, timeStamp } = timeRecordArrayCopy[i].event.detail;
156
+ const previousTime = timeStamp || timeRecordArrayCopy[i].timeStamp;
157
+ const loadTime = currentTime - previousTime;
158
+ const request = {
159
+ requestType: 'xhr',
160
+ responseURL,
161
+ status,
162
+ loadTime,
163
+ statusText,
164
+ reason: '',
165
+ detail: '',
166
+ };
167
+ if (loadTime && loadTime > XMLHttpRequestTimeout) {
168
+ request.reason = 'slow';
169
+ request.detail = `request is too slow, XMLHttpRequestTimeout: ${XMLHttpRequestTimeout}`;
170
+ }
171
+ else if (status && status >= 300 && statusText && statusText.toLowerCase() !== 'ok') {
172
+ request.reason = 'failed';
173
+ request.detail = `request is failed, status: ${status}, statusText: ${statusText}`;
174
+ }
175
+ else ;
176
+ if (request.reason) {
177
+ if (!tempUrlInfo[responseURL]) {
178
+ tempUrlInfo[responseURL] = true;
179
+ setTimeout(() => {
180
+ delete tempUrlInfo[responseURL];
181
+ }, 10);
182
+ setReportValue('error', null);
183
+ report(Object.assign(Object.assign({}, getReport()), {
184
+ page: location.href,
185
+ originPage: location.href,
186
+ type: 'request',
187
+ req: request,
188
+ }));
189
+ }
190
+ }
191
+ }
192
+ // 当前请求成功后就在数组中移除掉
193
+ timeRecordArray.splice(i, 1);
194
+ }
195
+ });
196
+ };
197
+ const hackFetch = (XMLHttpRequestTimeout) => {
198
+ XMLHttpRequestTimeout = typeof XMLHttpRequestTimeout === 'number' ? XMLHttpRequestTimeout : 1000;
199
+ if (typeof window.fetch === 'function') {
200
+ const __fetch__ = window.fetch;
201
+ window.__fetch__ = __fetch__;
202
+ window.fetch = function (t, ...args) {
203
+ const begin = Date.now();
204
+ //禁用数组的扩展运算符,否则portal的生产环境会报Uncaught TypeError: Object(...) is not a function,
205
+ //编译后的__spreadArray函数有问题,而这个函数来自于tslib.具体报错原因不知.
206
+ //其他平台使用数组的扩展运算符没有问题,比如前端监控平台的管理界面
207
+ const params = [].concat(t).concat(args);
208
+ return __fetch__
209
+ .apply(window, params)
210
+ .then(function (res) {
211
+ const response = res.clone();
212
+ const headers = response.headers;
213
+ if (headers && typeof headers.get === 'function') {
214
+ const ct = headers.get('content-type');
215
+ if (ct && !/(text)|(json)/.test(ct)) {
216
+ return res;
217
+ }
218
+ }
219
+ const loadTime = Date.now() - begin;
220
+ response
221
+ .text()
222
+ .then(function (result) {
223
+ const { url, status, statusText, ok } = response;
224
+ const request = {
225
+ requestType: 'fetch',
226
+ responseURL: url,
227
+ status,
228
+ loadTime,
229
+ statusText,
230
+ reason: '',
231
+ detail: '',
232
+ };
233
+ if (!ok || status >= 300) {
234
+ request.reason = 'failed';
235
+ request.detail = `request is failed, status: ${status}, statusText: ${statusText}`;
236
+ }
237
+ else if (loadTime > XMLHttpRequestTimeout) {
238
+ request.reason = 'slow';
239
+ request.detail = `request is too slow, XMLHttpRequestTimeout: ${XMLHttpRequestTimeout}, result: ${result === null || result === void 0 ? void 0 : result.slice(0, 500)}`;
240
+ }
241
+ if (request.reason) {
242
+ if (!tempUrlInfo[url]) {
243
+ tempUrlInfo[url] = true;
244
+ setTimeout(() => {
245
+ delete tempUrlInfo[url];
246
+ }, 10);
247
+ setReportValue('error', null);
248
+ report(Object.assign(Object.assign({}, getReport()), {
249
+ page: location.href,
250
+ originPage: location.href,
251
+ type: 'request',
252
+ req: request,
253
+ }));
254
+ }
255
+ }
256
+ })
257
+ .catch((err) => {
258
+ // eslint-disable-next-line no-console
259
+ console.log('hackFetch response.text() err', err);
260
+ });
261
+ return res;
262
+ })
263
+ .catch((err) => {
264
+ // eslint-disable-next-line no-console
265
+ console.log('hackFetch err', err);
266
+ });
267
+ };
268
+ }
269
+ };
270
+
271
+ export { getReport, hackFetch, initReport, recordXMLHttpRequestLog, setReportValue };
@@ -0,0 +1,33 @@
1
+ import { IConfig } from '../types/config';
2
+ /** */
3
+ /**
4
+ * @description config配置项默认值
5
+ */
6
+ export declare const Config: IConfig;
7
+ /**
8
+ * @description 设置config配置项
9
+ * @param config 配置项
10
+ */
11
+ export declare function setConfig(config: IConfig): void;
12
+ /**
13
+ * @description 设置config配置项
14
+ * @param config 配置项
15
+ */
16
+ export declare function setConfigValue<T extends IConfig, K extends keyof T>(key: K, value: T[K]): void;
17
+ /**
18
+ * @description 获取通过key值获取配置项value
19
+ * @param key
20
+ * @returns value的值
21
+ */
22
+ export declare function getConfigValue(key: keyof IConfig): string | number | boolean | {
23
+ urls?: string[] | undefined;
24
+ errors?: (string | {
25
+ regExp: boolean;
26
+ /**
27
+ * @description 设置config配置项
28
+ * @param config 配置项
29
+ */
30
+ input: string;
31
+ })[] | undefined;
32
+ apis?: string[] | undefined;
33
+ } | undefined;
@@ -0,0 +1,32 @@
1
+ /*
2
+ * @Author: Mark.Zhang
3
+ * @Description 配置项相关参数及方法
4
+ */
5
+ /** */
6
+ /**
7
+ * @description config配置项默认值
8
+ */
9
+ const Config = {
10
+ reportUrl: '',
11
+ projectID: '',
12
+ maxLength: 1000,
13
+ spa: false,
14
+ hash: false,
15
+ enableBehavior: true,
16
+ enableError: true,
17
+ enableVisualTrack: false,
18
+ ignore: {
19
+ urls: [],
20
+ errors: [],
21
+ apis: [],
22
+ },
23
+ };
24
+ /**
25
+ * @description 设置config配置项
26
+ * @param config 配置项
27
+ */
28
+ function setConfig(config) {
29
+ Object.assign(Config, config);
30
+ }
31
+
32
+ export { Config, setConfig };
@@ -0,0 +1,2 @@
1
+ export declare const shuyunTrackId = "shuyun-track-id";
2
+ export declare const shuyunTrackSessionId = "shuyun-track-session-id";
@@ -0,0 +1,4 @@
1
+ const shuyunTrackId = 'shuyun-track-id';
2
+ const shuyunTrackSessionId = 'shuyun-track-session-id';
3
+
4
+ export { shuyunTrackId, shuyunTrackSessionId };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @description 错误事件触发后的操作
3
+ */
4
+ export declare function handleError(event: ErrorEvent | Event | PromiseRejectionEvent): void;
5
+ /**
6
+ * @description 设置错误信息
7
+ */
8
+ export declare function setCaughtError(event: ErrorEvent): void;
9
+ /**
10
+ * @description 设置异步错误信息
11
+ */
12
+ export declare function setPromiseError(event: PromiseRejectionEvent): void;
13
+ /**
14
+ * @description 设置资源错误信息
15
+ */
16
+ export declare function setResourceError(event: Event): void;
17
+ /**
18
+ * 忽略的error
19
+ * @param errorMsg 错误信息
20
+ */
21
+ export declare function ignoreError(errorMsg: string): boolean;
22
+ export declare function getUserEvents(notUseCache: boolean): string | undefined;
23
+ export declare function getFullScreenShoot(filename: string | undefined): Promise<File>;
@@ -0,0 +1,181 @@
1
+ import * as rrweb from 'rrweb';
2
+ import ErrorStackParser from 'error-stack-parser';
3
+ import html2canvas from 'html2canvas';
4
+ import { v4 } from 'uuid';
5
+ import { report } from '../reporter.js';
6
+ import { setReportValue, getReport } from '../config/global.js';
7
+ import { Config } from '../config/index.js';
8
+
9
+ /*
10
+ * @Author: Mark.Zhang
11
+ * @Date: 2020-10-26 11:10:04
12
+ * @Description 资源加载错误及js错误相关的处理方法
13
+ */
14
+ const eventsMatrix = [[]];
15
+ rrweb.record({
16
+ emit(event, isCheckout) {
17
+ // isCheckout 是一个标识,告诉你重新制作了快照
18
+ if (isCheckout) {
19
+ eventsMatrix.push([]);
20
+ }
21
+ if (eventsMatrix.length > 2) {
22
+ eventsMatrix.shift();
23
+ }
24
+ const lastEvents = eventsMatrix[eventsMatrix.length - 1];
25
+ lastEvents.push(event);
26
+ },
27
+ // 每30秒重新制作快照
28
+ checkoutEveryNms: 30 * 1000,
29
+ });
30
+ /**
31
+ * @description 错误事件触发后的操作
32
+ */
33
+ function handleError(event) {
34
+ setReportValue('type', 'error');
35
+ if (event.type === 'error') {
36
+ if (event instanceof ErrorEvent) {
37
+ setCaughtError(event);
38
+ }
39
+ else {
40
+ setResourceError(event);
41
+ }
42
+ }
43
+ else if (event.type === 'unhandledrejection') {
44
+ setPromiseError(event);
45
+ }
46
+ }
47
+ /**
48
+ * @description 设置错误信息
49
+ */
50
+ function setCaughtError(event) {
51
+ var _a, _b;
52
+ if (ignoreError(event.message)) {
53
+ return;
54
+ }
55
+ let filename = event.filename, line = event.lineno, column = event.colno, functionName = '', stackFrame = JSON.stringify([]);
56
+ if (event.error && event.error.stack) {
57
+ const error = ErrorStackParser.parse(event.error);
58
+ stackFrame = JSON.stringify(error);
59
+ const info = error.find((item) => item.fileName && !/node_modules|vendor|bundle/.test(item.fileName));
60
+ if (info) {
61
+ filename = info.fileName;
62
+ line = Number(info.lineNumber);
63
+ column = Number(info.columnNumber);
64
+ functionName = info.functionName || '';
65
+ }
66
+ }
67
+ setReportValue('error', {
68
+ subType: 'js',
69
+ line,
70
+ column,
71
+ message: event.message,
72
+ filename,
73
+ functionName,
74
+ stack: ((_b = (_a = event === null || event === void 0 ? void 0 : event.error) === null || _a === void 0 ? void 0 : _a.stack) === null || _b === void 0 ? void 0 : _b.substring(0, Config.maxLength)) || '',
75
+ stackFrame,
76
+ events: getUserEvents(false),
77
+ });
78
+ report(getReport());
79
+ }
80
+ /**
81
+ * @description 设置异步错误信息
82
+ */
83
+ function setPromiseError(event) {
84
+ let message = event.reason;
85
+ if (event.reason && typeof event.reason === 'object') {
86
+ message = JSON.stringify(event.reason);
87
+ if (message === '{}') {
88
+ message = event.reason.stack;
89
+ }
90
+ }
91
+ if (ignoreError(message)) {
92
+ return;
93
+ }
94
+ setReportValue('error', {
95
+ subType: 'async',
96
+ message,
97
+ events: getUserEvents(false),
98
+ });
99
+ report(getReport());
100
+ }
101
+ /**
102
+ * @description 设置资源错误信息
103
+ */
104
+ function setResourceError(event) {
105
+ const target = event.target;
106
+ if (ignoreError(target.outerHTML)) {
107
+ return;
108
+ }
109
+ setReportValue('error', {
110
+ subType: 'resource',
111
+ message: target.outerHTML,
112
+ filename: target.src || target.currentSrc,
113
+ stack: target.localName.toUpperCase().substring(0, Config.maxLength),
114
+ events: getUserEvents(false),
115
+ });
116
+ report(getReport());
117
+ }
118
+ /**
119
+ * 忽略的error
120
+ * @param errorMsg 错误信息
121
+ */
122
+ function ignoreError(errorMsg) {
123
+ var _a;
124
+ const errors = ((_a = Config === null || Config === void 0 ? void 0 : Config.ignore) === null || _a === void 0 ? void 0 : _a.errors) || [];
125
+ const existIgnoreError = errors.findIndex((item) => {
126
+ if (typeof item === 'string') {
127
+ return item === errorMsg;
128
+ }
129
+ else if (Object.prototype.toString.call(item) === '[object Object]') {
130
+ if (item.regExp && typeof item.input === 'string') {
131
+ const regex = new RegExp(item.input, 'g');
132
+ return regex.test(errorMsg);
133
+ }
134
+ return false;
135
+ }
136
+ else {
137
+ return false;
138
+ }
139
+ });
140
+ return existIgnoreError > -1;
141
+ }
142
+ /**
143
+ * 获取用户最近得操作,视频录像时间应当在30秒到60秒之间
144
+ */
145
+ let lastGetTime = Date.now(); //缓存操作录像,防止段时间内大量获取录像数据导滞内存溢出
146
+ function getUserEvents(notUseCache) {
147
+ let events;
148
+ if (Date.now() - lastGetTime > 2000 || notUseCache) {
149
+ lastGetTime = Date.now();
150
+ if (eventsMatrix.length >= 2) {
151
+ const finalVideoData = eventsMatrix[eventsMatrix.length - 1];
152
+ events = JSON.stringify(eventsMatrix[eventsMatrix.length - 2].concat(finalVideoData));
153
+ if (events.length > 1024 * 1024 * 15 && JSON.stringify(finalVideoData).length > 1024 * 1024) {
154
+ //如果录像数据量太大且最后那个分片的数据量不算小,那么缩短上报的录像时长
155
+ events = JSON.stringify(eventsMatrix[eventsMatrix.length - 1]);
156
+ }
157
+ }
158
+ else {
159
+ events = JSON.stringify(eventsMatrix);
160
+ }
161
+ }
162
+ return events;
163
+ }
164
+ function getFullScreenShoot(filename) {
165
+ const name = filename || v4();
166
+ return html2canvas(document.body).then(function (canvas) {
167
+ var _a;
168
+ const urlData = canvas.toDataURL('image/png', 1);
169
+ const bytes = window.atob(urlData.split(',')[1]);
170
+ const mime = (_a = urlData.split(',')[0].match(/:(.*?);/)) === null || _a === void 0 ? void 0 : _a[1];
171
+ const ab = new ArrayBuffer(bytes.length);
172
+ const ia = new Uint8Array(ab);
173
+ for (let i = 0; i < bytes.length; i++) {
174
+ ia[i] = bytes.charCodeAt(i);
175
+ }
176
+ const file = new File([ab], `${name}.png`, { type: mime });
177
+ return file;
178
+ });
179
+ }
180
+
181
+ export { getFullScreenShoot, getUserEvents, handleError, ignoreError, setCaughtError, setPromiseError, setResourceError };
@@ -0,0 +1,3 @@
1
+ export * as pv from './pv';
2
+ export * as error from './error';
3
+ export * as userActivity from './user-activity';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @description 派发pushState, replaceState 的监听
3
+ * @param type
4
+ */
5
+ export declare function _history(type: 'pushState' | 'replaceState'): () => void;
6
+ /**
7
+ * @description 处理hash变化
8
+ * @param e hash事件
9
+ */
10
+ export declare function handleHashChange(__e: Event): void;
11
+ /**
12
+ * @description 处理history变化
13
+ * @param e history事件
14
+ */
15
+ export declare function handleHistoryChange(__e: Event): void;
16
+ export declare function setPVTime(): void;
17
+ /**
18
+ * @description 添加路由栈信息
19
+ * @param page 页面信息
20
+ */
21
+ export declare function setRouteStack(page: string | string[]): {
22
+ originPage: string;
23
+ page: string;
24
+ };
@@ -0,0 +1,99 @@
1
+ import { setReportValue, getReport } from '../config/global.js';
2
+ import { report } from '../reporter.js';
3
+ import { Config } from '../config/index.js';
4
+ import { visualTrackFunc } from '../utils/index.js';
5
+
6
+ /* eslint-disable prefer-rest-params */
7
+ /** 路由栈数组,存储路由变化信息 */
8
+ let routerStack = [];
9
+ /** 路由触发的时间 */
10
+ let time = 0;
11
+ /**
12
+ * @description 派发pushState, replaceState 的监听
13
+ * @param type
14
+ */
15
+ function _history(type) {
16
+ const origin = history[type];
17
+ return function () {
18
+ // @ts-ignore
19
+ const r = origin.apply(this, arguments);
20
+ const e = new Event(type);
21
+ // @ts-ignore
22
+ e.arguments = arguments;
23
+ window.dispatchEvent(e);
24
+ return r;
25
+ };
26
+ }
27
+ /**
28
+ * @description 处理history变化
29
+ * @param e history事件
30
+ */
31
+ function handleHistoryChange(__e) {
32
+ visualTrackFunc();
33
+ setReport();
34
+ }
35
+ /**
36
+ * 设置信息并触发上报
37
+ */
38
+ function setReport() {
39
+ let pageUrl = location.href;
40
+ /**
41
+ * 此判断的逻辑是因为spa且hash模式下,路由搜索参数更改也会触发handleHistoryChange事件
42
+ * 所以路由栈中的信息只保存无搜索参数的地址
43
+ */
44
+ if (Config.hash && Config.spa) {
45
+ const currentUrlSplit = location.href.split('#');
46
+ let currentUrlPostfix = '';
47
+ if (currentUrlSplit[1]) {
48
+ const index = currentUrlSplit[1].indexOf('?');
49
+ if (index > -1) {
50
+ currentUrlPostfix = currentUrlSplit[1].substring(0, index);
51
+ }
52
+ else {
53
+ currentUrlPostfix = currentUrlSplit[1];
54
+ }
55
+ }
56
+ const currentUrlResult = `${currentUrlSplit[0]}#${currentUrlPostfix}`;
57
+ pageUrl = currentUrlResult;
58
+ }
59
+ const { originPage, page } = setRouteStack(pageUrl);
60
+ // 愿页面等于当前页面,说明无路由变更。只有spa且hash模式下才会出现
61
+ if (originPage === page) {
62
+ return;
63
+ }
64
+ const currentTime = new Date().getTime();
65
+ // 页面停留时间
66
+ const stayTime = currentTime - time;
67
+ // 设置触发路由变化的时间
68
+ time = currentTime;
69
+ // 设置信息
70
+ setReportValue('originPage', originPage);
71
+ setReportValue('page', page);
72
+ setReportValue('type', 'pv');
73
+ setReportValue('pv', { stayTime });
74
+ // 上报数据
75
+ report(getReport());
76
+ }
77
+ function setPVTime() {
78
+ time = new Date().getTime();
79
+ }
80
+ /**
81
+ * @description 添加路由栈信息
82
+ * @param page 页面信息
83
+ */
84
+ function setRouteStack(page) {
85
+ if (typeof page === 'string') {
86
+ routerStack.push(page);
87
+ }
88
+ else {
89
+ routerStack = routerStack.concat(page);
90
+ }
91
+ // 路由栈只需保留两个来作为源页面和当前页面
92
+ routerStack = routerStack.slice(-2);
93
+ return {
94
+ originPage: routerStack[0],
95
+ page: routerStack[1] || routerStack[0],
96
+ };
97
+ }
98
+
99
+ export { _history, handleHistoryChange, setPVTime, setRouteStack };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @description 点击事件触发后的操作
3
+ */
4
+ export declare function handleClick(event: Event): void;
5
+ /**
6
+ * @description 点击触发失焦后的操作
7
+ */
8
+ export declare function handleBlur(event: Event): void;
9
+ export declare function handleScroll(_event: Event): void;