@tmsfe/tms-core 0.0.39 → 0.0.42
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/package.json +1 -1
- package/src/index-proxy.js +2 -0
- package/src/index.js +2 -0
- package/src/report/formatV1.ts +7 -5
- package/src/report/formatV2.ts +4 -3
- package/src/report/helper.ts +53 -49
- package/src/report/proxy/component.ts +42 -0
- package/src/report/proxy/helper.ts +133 -0
- package/src/report/proxy/index.ts +20 -0
- package/src/report/proxy/page.ts +80 -0
- package/src/report/types.ts +2 -1
package/package.json
CHANGED
package/src/index-proxy.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import syncApi from './syncfnmanager';
|
|
9
|
+
import AutoReport from './report/proxy/index';
|
|
9
10
|
import md5 from './md5';
|
|
10
11
|
import { rpxToPx } from './rpx';
|
|
11
12
|
import { serialize } from './objUtils';
|
|
@@ -39,6 +40,7 @@ function invoke(obj, funcName, args) {
|
|
|
39
40
|
function initProxy(appObj, options) {
|
|
40
41
|
app = appObj;
|
|
41
42
|
initOptions = options;
|
|
43
|
+
AutoReport.init();
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
function awaitTMS() {
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Request from './request';
|
|
2
|
+
import AutoReport from './report/proxy/index';
|
|
2
3
|
import Reporter from './report/index';
|
|
3
4
|
import { getConfig } from './config';
|
|
4
5
|
import syncApi from './syncfnmanager';
|
|
@@ -101,6 +102,7 @@ const init = (options = {}) => {
|
|
|
101
102
|
setEnvInfo(envInfo);
|
|
102
103
|
setAppPagePaths(appPagePaths, homePage);
|
|
103
104
|
Reporter.init(envInfo);
|
|
105
|
+
AutoReport.init();
|
|
104
106
|
Request.defaultHost = defaultHost;
|
|
105
107
|
// 初始化云环境
|
|
106
108
|
wx.cloud.init({ env: cloudEnvId });
|
package/src/report/formatV1.ts
CHANGED
|
@@ -9,13 +9,15 @@ import helper from './helper';
|
|
|
9
9
|
* 获取埋点的基础字段
|
|
10
10
|
*/
|
|
11
11
|
function getBaseData(deviceData: IDeviceData): DataItem[] {
|
|
12
|
-
const arr = new Array<string>(helper.dataArrLen);
|
|
13
12
|
const { networkType, location } = deviceData;
|
|
14
13
|
const { appVersion, client } = helper.getInitOptions();
|
|
14
|
+
const { host } = helper.getSystemInfo();
|
|
15
|
+
const arr = new Array<string>(helper.dataArrLen);
|
|
15
16
|
// todo: 如何区分新旧埋点?新:f20不为空,旧:f20为空
|
|
16
17
|
// ++++++++++++++++++++++++++字段列表++++++++++++++++++++++++++
|
|
17
18
|
// 0: log_time,日志入库时间
|
|
18
|
-
// 1: access_time
|
|
19
|
+
// 1: access_time,埋点触发时间
|
|
20
|
+
arr[1] = Date.now().toString();
|
|
19
21
|
// 2: user_ip,前端无需赋值
|
|
20
22
|
// 3: qimei,灯塔中的用户ID
|
|
21
23
|
// 4: imei,Android手机的imei IOS系统中的idfv 车联网中的wecarid
|
|
@@ -42,14 +44,14 @@ function getBaseData(deviceData: IDeviceData): DataItem[] {
|
|
|
42
44
|
// 18: f18,city - 城市
|
|
43
45
|
arr[18] = location.cityName;
|
|
44
46
|
// 19: f19,当前小程序运行的宿主环境
|
|
45
|
-
arr[19] = helper.
|
|
47
|
+
arr[19] = helper.convert2String(host);
|
|
46
48
|
// 28: f28,sinan、mycar等
|
|
47
49
|
arr[28] = client;
|
|
48
50
|
// 29: f29,小程序场景值
|
|
49
51
|
arr[29] = helper.getAppScene();
|
|
50
52
|
// 33: f33,系统信息
|
|
51
53
|
arr[33] = helper.getSystemInfoString();
|
|
52
|
-
// 36: f36
|
|
54
|
+
// 36: f36,小程序启动时的url和参数
|
|
53
55
|
arr[36] = helper.getLaunchOptionsString();
|
|
54
56
|
// --------------------------字段列表--------------------------
|
|
55
57
|
return arr;
|
|
@@ -83,7 +85,7 @@ function formatData(data: IOldParams): Promise<DataItem[]> {
|
|
|
83
85
|
* 格式化快速上报埋点数据,不依赖用户位置
|
|
84
86
|
*/
|
|
85
87
|
function formatFastData(data: IOldParams): DataItem[] {
|
|
86
|
-
return jointData(data, helper.
|
|
88
|
+
return jointData(data, helper.getCacheDeviceData());
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
export default {
|
package/src/report/formatV2.ts
CHANGED
|
@@ -16,7 +16,8 @@ function getBaseData(deviceData: IDeviceData): { arr: DataItem[], nextIndex: num
|
|
|
16
16
|
// todo: 如何区分新旧埋点?新:f20不为空,旧:f20为空
|
|
17
17
|
// ++++++++++++++++++++++++++字段列表++++++++++++++++++++++++++
|
|
18
18
|
// 0: log_time,日志入库时间
|
|
19
|
-
// 1: access_time
|
|
19
|
+
// 1: access_time,埋点触发时间
|
|
20
|
+
arr[1] = Date.now().toString();
|
|
20
21
|
// 2: user_ip,前端无需赋值
|
|
21
22
|
// 3: qimei,灯塔中的用户ID
|
|
22
23
|
// 4: imei,Android手机的imei IOS系统中的idfv 车联网中的wecarid
|
|
@@ -47,7 +48,7 @@ function getBaseData(deviceData: IDeviceData): { arr: DataItem[], nextIndex: num
|
|
|
47
48
|
arr[20] = client;
|
|
48
49
|
// 21: f21,小程序场景值
|
|
49
50
|
arr[21] = helper.getAppScene();
|
|
50
|
-
// 22: f22
|
|
51
|
+
// 22: f22,小程序启动时的url和参数
|
|
51
52
|
arr[22] = helper.getLaunchOptionsString();
|
|
52
53
|
// 23: f23,当前页面的url
|
|
53
54
|
arr[23] = page.route;
|
|
@@ -92,7 +93,7 @@ function formatData(data: any[]): Promise<DataItem[]> {
|
|
|
92
93
|
* 格式化快速上报埋点数据,不依赖用户位置
|
|
93
94
|
*/
|
|
94
95
|
function formatFastData(data: any[]): DataItem[] {
|
|
95
|
-
return jointData(data, helper.
|
|
96
|
+
return jointData(data, helper.getCacheDeviceData());
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
export default {
|
package/src/report/helper.ts
CHANGED
|
@@ -18,6 +18,16 @@ let initOptions: IInitOptions;
|
|
|
18
18
|
function init(options: IInitOptions): void {
|
|
19
19
|
if (!initOptions) {
|
|
20
20
|
initOptions = options;
|
|
21
|
+
|
|
22
|
+
// 从上次缓存中读取,方便首次Report可以先带上
|
|
23
|
+
const loc = wx.getStorageSync('home.location_city') || wx.getStorageSync('home.city');
|
|
24
|
+
deviceData = {
|
|
25
|
+
networkType: '',
|
|
26
|
+
location: {
|
|
27
|
+
province: loc?.province as string,
|
|
28
|
+
cityName: loc?.cityName as string,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
21
31
|
}
|
|
22
32
|
}
|
|
23
33
|
|
|
@@ -38,8 +48,7 @@ function getSystemInfo(): ISystemInfo {
|
|
|
38
48
|
const system = syncApi.getSystemInfoSync() as any;
|
|
39
49
|
// eslint-disable-next-line
|
|
40
50
|
const { model = '', version: wxVersion = '', platform = '', SDKVersion = '', host } = system;
|
|
41
|
-
|
|
42
|
-
systemInfo = { model, wxVersion, platform, SDKVersion, host: hostStr };
|
|
51
|
+
systemInfo = { model, wxVersion, platform, SDKVersion, host };
|
|
43
52
|
}
|
|
44
53
|
return systemInfo;
|
|
45
54
|
}
|
|
@@ -61,6 +70,9 @@ function getSystemInfoString(): string {
|
|
|
61
70
|
* @param value
|
|
62
71
|
*/
|
|
63
72
|
function convert2String(value: any): string {
|
|
73
|
+
if (value === null || value === undefined) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
64
76
|
const type = typeof value;
|
|
65
77
|
if (type === 'string') {
|
|
66
78
|
return value;
|
|
@@ -96,8 +108,8 @@ let launchOptions: string | null = null;
|
|
|
96
108
|
*/
|
|
97
109
|
function getLaunchOptionsString(): string {
|
|
98
110
|
if (launchOptions === null) {
|
|
99
|
-
const obj = syncApi.getLaunchOptionsSync()
|
|
100
|
-
launchOptions =
|
|
111
|
+
const obj = syncApi.getLaunchOptionsSync();
|
|
112
|
+
launchOptions = JSON.stringify(obj);
|
|
101
113
|
}
|
|
102
114
|
return launchOptions;
|
|
103
115
|
}
|
|
@@ -107,7 +119,7 @@ function getLaunchOptionsString(): string {
|
|
|
107
119
|
*/
|
|
108
120
|
function getLaunchFrom(): string {
|
|
109
121
|
const obj = syncApi.getLaunchOptionsSync() as any;
|
|
110
|
-
return obj.query?.from
|
|
122
|
+
return obj.query?.from;
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
/**
|
|
@@ -142,46 +154,8 @@ function canReport(): boolean {
|
|
|
142
154
|
*/
|
|
143
155
|
const dataArrLen = 41;
|
|
144
156
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
location: {
|
|
148
|
-
province: null,
|
|
149
|
-
cityName: null,
|
|
150
|
-
},
|
|
151
|
-
} as any as IDeviceData;
|
|
152
|
-
|
|
153
|
-
let networkPromise: Promise<string> | null = null;
|
|
154
|
-
|
|
155
|
-
function getNetworkType(): Promise<string> {
|
|
156
|
-
if (networkPromise === null) {
|
|
157
|
-
networkPromise = new Promise((resolve) => {
|
|
158
|
-
wx.getNetworkType({
|
|
159
|
-
success: (res: any) => resolve(res.networkType),
|
|
160
|
-
fail: () => resolve('unknown'),
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
return networkPromise;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
let locPromise: Promise<ILocation> | null = null;
|
|
168
|
-
|
|
169
|
-
function getLocation(): Promise<ILocation> {
|
|
170
|
-
if (locPromise === null) {
|
|
171
|
-
locPromise = new Promise<ILocation>((resolve) => {
|
|
172
|
-
const LOC = getTms().getLocationManager();
|
|
173
|
-
LOC.getIpLocation()
|
|
174
|
-
.then((res: any) => {
|
|
175
|
-
const ad = res.ad_info;
|
|
176
|
-
resolve({ province: ad.province, cityName: ad.city });
|
|
177
|
-
})
|
|
178
|
-
.catch(() => {
|
|
179
|
-
resolve({ province: '', cityName: '' });
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
return locPromise;
|
|
184
|
-
}
|
|
157
|
+
// 缓存设备等信息,方便FastReport使用
|
|
158
|
+
let deviceData: IDeviceData;
|
|
185
159
|
|
|
186
160
|
let devicePromise: Promise<IDeviceData> | null = null;
|
|
187
161
|
|
|
@@ -191,15 +165,45 @@ let devicePromise: Promise<IDeviceData> | null = null;
|
|
|
191
165
|
function getDeviceData(): Promise<IDeviceData> {
|
|
192
166
|
if (devicePromise === null) {
|
|
193
167
|
devicePromise = new Promise<IDeviceData>((resolve) => {
|
|
194
|
-
Promise
|
|
195
|
-
|
|
196
|
-
|
|
168
|
+
const networkPromise = new Promise<void>((resolve) => {
|
|
169
|
+
wx.getNetworkType({
|
|
170
|
+
success: (res: any) => {
|
|
171
|
+
deviceData.networkType = res.networkType;
|
|
172
|
+
resolve();
|
|
173
|
+
},
|
|
174
|
+
fail: () => resolve(),
|
|
175
|
+
});
|
|
197
176
|
});
|
|
177
|
+
|
|
178
|
+
const locPromise = new Promise<void>((resolve) => {
|
|
179
|
+
// 首次如果有缓存则先用缓存,请求后再更新给下次用
|
|
180
|
+
if (deviceData.location.province) {
|
|
181
|
+
resolve();
|
|
182
|
+
}
|
|
183
|
+
const LOC = getTms().getLocationManager();
|
|
184
|
+
LOC.getIpLocation()
|
|
185
|
+
.then((res: any) => {
|
|
186
|
+
const ad = res.ad_info;
|
|
187
|
+
deviceData.location.province = ad.province;
|
|
188
|
+
deviceData.location.cityName = ad.city;
|
|
189
|
+
resolve();
|
|
190
|
+
})
|
|
191
|
+
.catch(() => resolve());
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
Promise.all([networkPromise, locPromise]).then(() => resolve(deviceData));
|
|
198
195
|
});
|
|
199
196
|
}
|
|
200
197
|
return devicePromise;
|
|
201
198
|
}
|
|
202
199
|
|
|
200
|
+
/**
|
|
201
|
+
* 获取缓存的设备信息(网络状况,位置信息)
|
|
202
|
+
*/
|
|
203
|
+
function getCacheDeviceData(): IDeviceData {
|
|
204
|
+
return deviceData;
|
|
205
|
+
}
|
|
206
|
+
|
|
203
207
|
export default {
|
|
204
208
|
init,
|
|
205
209
|
getTms,
|
|
@@ -213,6 +217,6 @@ export default {
|
|
|
213
217
|
getAppScene,
|
|
214
218
|
canReport,
|
|
215
219
|
dataArrLen,
|
|
216
|
-
defaultDevice,
|
|
217
220
|
getDeviceData,
|
|
221
|
+
getCacheDeviceData,
|
|
218
222
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 负责组件的全埋点
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import helper from './helper';
|
|
6
|
+
|
|
7
|
+
let originalComponent: any = null;
|
|
8
|
+
|
|
9
|
+
// 劫持其他事件
|
|
10
|
+
function proxyHandle(componentName: string, methods: any, methodName: string): void {
|
|
11
|
+
const original = methods[methodName];
|
|
12
|
+
// eslint-disable-next-line no-param-reassign
|
|
13
|
+
methods[methodName] = function (...args: any[]): any {
|
|
14
|
+
// 执行原函数之后再发埋点
|
|
15
|
+
return helper.executeFunc(this, original, args, () => {
|
|
16
|
+
const data = helper.serializeData(this.data);
|
|
17
|
+
helper.reportData(`Component_${componentName}`, methodName, data);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 劫持Page
|
|
23
|
+
function init(): void {
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
originalComponent = Component;
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
Component = function (options: any) {
|
|
28
|
+
// 只有tms的页面才需要上报
|
|
29
|
+
if (options.tmsAutoReport) {
|
|
30
|
+
// tmsReportEvents是由工具在编译时分析出的要上报的事件列表
|
|
31
|
+
const methods = options.methods || {};
|
|
32
|
+
for (const name of options.tmsReportEvents) {
|
|
33
|
+
proxyHandle(options.tmsComponentName, methods, name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
originalComponent(options);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default {
|
|
41
|
+
init,
|
|
42
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动埋点辅助类
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
let reporter: any = null;
|
|
6
|
+
|
|
7
|
+
function reportData(...args: any[]): any {
|
|
8
|
+
if (reporter === null) {
|
|
9
|
+
// 如果是在app.js的onLaunch中调用,则没有getApp().tms为空
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
const tms = getApp()?.tms || wx.tms;
|
|
12
|
+
reporter = tms.getReporter();
|
|
13
|
+
}
|
|
14
|
+
// console.log('自动埋点:', ...args);
|
|
15
|
+
reporter.report2(...args);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 空函数
|
|
20
|
+
*/
|
|
21
|
+
function emptyFunc(): void {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 执行函数
|
|
26
|
+
* @param context
|
|
27
|
+
* @param func
|
|
28
|
+
* @param args
|
|
29
|
+
* @param doneCallback
|
|
30
|
+
*/
|
|
31
|
+
function executeFunc(context: any, func: Function, args: any[], doneCallback: Function): any {
|
|
32
|
+
const value = func.apply(context, args);
|
|
33
|
+
// 如果不会返回promise
|
|
34
|
+
if (!value || !value.then || typeof value.then !== 'function') {
|
|
35
|
+
doneCallback();
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return value.then((res: any) => {
|
|
40
|
+
doneCallback();
|
|
41
|
+
return res;
|
|
42
|
+
}).catch((err: any) => {
|
|
43
|
+
doneCallback();
|
|
44
|
+
return Promise.reject(err);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const maxDepth = 5;
|
|
49
|
+
const maxArrLen = 10;
|
|
50
|
+
const maxStrLen = 30;
|
|
51
|
+
const maxFieldLen = 20;
|
|
52
|
+
|
|
53
|
+
function isBasicsType(obj: any): { isBasics: boolean, value: any } {
|
|
54
|
+
const type = typeof obj;
|
|
55
|
+
if (obj === null || obj === undefined
|
|
56
|
+
|| type === 'number' || type === 'boolean' || type === 'bigint') {
|
|
57
|
+
return { isBasics: true, value: obj };
|
|
58
|
+
}
|
|
59
|
+
if (type === 'string') {
|
|
60
|
+
const value = obj.substr(0, maxStrLen);
|
|
61
|
+
return { isBasics: true, value };
|
|
62
|
+
}
|
|
63
|
+
if (type === 'function' || type === 'symbol') {
|
|
64
|
+
return { isBasics: true, value: undefined };
|
|
65
|
+
}
|
|
66
|
+
return { isBasics: false, value: obj };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isArrayType(obj: any): { isArray: boolean, value: any } {
|
|
70
|
+
if (Array.isArray(obj)) {
|
|
71
|
+
const arr = obj.slice(0, maxArrLen);
|
|
72
|
+
return { isArray: true, value: arr };
|
|
73
|
+
}
|
|
74
|
+
return { isArray: false, value: obj };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 深表克隆
|
|
79
|
+
* @param obj
|
|
80
|
+
* @param depth 当前克隆的深度
|
|
81
|
+
*/
|
|
82
|
+
function deepClone(obj: any, depth: number): any {
|
|
83
|
+
if (depth >= maxDepth) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const res1 = isBasicsType(obj);
|
|
87
|
+
if (res1.isBasics) {
|
|
88
|
+
return res1.value;
|
|
89
|
+
}
|
|
90
|
+
const res2 = isArrayType(obj);
|
|
91
|
+
if (res2.isArray) {
|
|
92
|
+
return res2.value.map((t: any) => deepClone(t, depth + 1));
|
|
93
|
+
}
|
|
94
|
+
const names = Object.getOwnPropertyNames(obj).slice(0, maxFieldLen);
|
|
95
|
+
const newObj = {};
|
|
96
|
+
for (const name of names) {
|
|
97
|
+
if (name === '__webviewId__') {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const value = obj[name];
|
|
101
|
+
const res1 = isBasicsType(value);
|
|
102
|
+
if (res1.isBasics) {
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
newObj[name] = res1.value;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const res2 = isArrayType(value);
|
|
108
|
+
if (res2.isArray) {
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
newObj[name] = res2.value.map((t: any) => deepClone(t, depth + 1));
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
// @ts-ignore
|
|
114
|
+
newObj[name] = deepClone(value, depth + 1);
|
|
115
|
+
}
|
|
116
|
+
return newObj;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 序列化data
|
|
121
|
+
* @param data
|
|
122
|
+
*/
|
|
123
|
+
function serializeData(data: any): string {
|
|
124
|
+
const obj = deepClone(data, 0);
|
|
125
|
+
return JSON.stringify(obj);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default {
|
|
129
|
+
reportData,
|
|
130
|
+
emptyFunc,
|
|
131
|
+
executeFunc,
|
|
132
|
+
serializeData,
|
|
133
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 负责页面和组件的全埋点
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import proxyPage from './page';
|
|
6
|
+
import proxyComponent from './component';
|
|
7
|
+
|
|
8
|
+
let isInit = false;
|
|
9
|
+
|
|
10
|
+
function init(): void {
|
|
11
|
+
if (!isInit) {
|
|
12
|
+
isInit = true;
|
|
13
|
+
proxyPage.init();
|
|
14
|
+
proxyComponent.init();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
init,
|
|
20
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 负责页面的全埋点
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import helper from './helper';
|
|
6
|
+
|
|
7
|
+
// 必须上报的生命周期函数
|
|
8
|
+
const mustLifeMethods = ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload'];
|
|
9
|
+
// 可能会有的生命周期函数
|
|
10
|
+
const maybeLifeMethods = [
|
|
11
|
+
'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage',
|
|
12
|
+
'onShareTimeline', 'onAddToFavorites', 'onPageScroll',
|
|
13
|
+
];
|
|
14
|
+
const lifeMethods = mustLifeMethods.concat(maybeLifeMethods);
|
|
15
|
+
|
|
16
|
+
let originalPage: any = null;
|
|
17
|
+
|
|
18
|
+
// 劫持Page的生命周期
|
|
19
|
+
function proxyLifeMethod(pageOptions: any, methodName: string): void {
|
|
20
|
+
if (!pageOptions[methodName] && !mustLifeMethods.includes(methodName)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const original = pageOptions[methodName] || helper.emptyFunc;
|
|
24
|
+
// eslint-disable-next-line no-param-reassign
|
|
25
|
+
pageOptions[methodName] = function (...args: any[]): any {
|
|
26
|
+
let elapsed: number | '' = ''; // 页面曝光时间
|
|
27
|
+
let showCount: number | '' = ''; // 曝光次数
|
|
28
|
+
if (methodName === 'onShow') {
|
|
29
|
+
this.tmsPageShowCount = (this.tmsPageShowCount || 0) + 1;
|
|
30
|
+
this.tmsPageShowTime = Date.now();
|
|
31
|
+
showCount = this.tmsPageShowCount;
|
|
32
|
+
} else if (methodName === 'onHide' || methodName === 'onUnload') {
|
|
33
|
+
elapsed = Date.now() - this.tmsPageShowTime;
|
|
34
|
+
this.tmsPageShowTime = 0;
|
|
35
|
+
showCount = this.tmsPageShowCount;
|
|
36
|
+
}
|
|
37
|
+
// 执行原函数之后再发埋点
|
|
38
|
+
return helper.executeFunc(this, original, args, () => {
|
|
39
|
+
const data = helper.serializeData(this.data);
|
|
40
|
+
helper.reportData(`Page_${methodName}`, data, showCount, elapsed);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 劫持其他事件
|
|
46
|
+
function proxyHandle(pageOptions: any, methodName: string): void {
|
|
47
|
+
const original = pageOptions[methodName] || helper.emptyFunc;
|
|
48
|
+
// eslint-disable-next-line no-param-reassign
|
|
49
|
+
pageOptions[methodName] = function (...args: any[]): any {
|
|
50
|
+
// 执行原函数之后再发埋点
|
|
51
|
+
return helper.executeFunc(this, original, args, () => {
|
|
52
|
+
const data = helper.serializeData(this.data);
|
|
53
|
+
helper.reportData(`Page_${methodName}`, data);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 劫持Page
|
|
59
|
+
function init(): void {
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
originalPage = Page;
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
Page = function (options: any) {
|
|
64
|
+
// 只有tms的页面才需要上报
|
|
65
|
+
if (options.tmsAutoReport) {
|
|
66
|
+
for (const methodName of lifeMethods) {
|
|
67
|
+
proxyLifeMethod(options, methodName);
|
|
68
|
+
}
|
|
69
|
+
// tmsReportEvents是由工具在编译时分析出的要上报的事件列表
|
|
70
|
+
for (const methodName of options.tmsReportEvents) {
|
|
71
|
+
proxyHandle(options, methodName);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
originalPage(options);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default {
|
|
79
|
+
init,
|
|
80
|
+
};
|