@tmsfe/tms-core 0.0.48 → 0.0.49

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmsfe/tms-core",
3
- "version": "0.0.48",
3
+ "version": "0.0.49",
4
4
  "description": "tms运行时框架",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,7 +9,7 @@ import helper from './helper';
9
9
  * 获取埋点的基础字段
10
10
  */
11
11
  function getBaseData(deviceData: IDeviceData): { arr: DataItem[], nextIndex: number } {
12
- const page = helper.getPageInfo() as IPage;
12
+ const { page, pageDepth } = helper.getPageInfo();
13
13
  const { networkType, location } = deviceData;
14
14
  const { appVersion, client } = helper.getInitOptions();
15
15
  const arr = new Array<string>(helper.dataArrLen);
@@ -54,7 +54,9 @@ function getBaseData(deviceData: IDeviceData): { arr: DataItem[], nextIndex: num
54
54
  arr[23] = page?.route;
55
55
  // 24: f24,当前页面的query
56
56
  arr[24] = JSON.stringify(page?.options);
57
- // 25 ~ 30: 预留字段给后续扩展使用
57
+ // 25: f25,当前页面深度
58
+ arr[25] = pageDepth.toString();
59
+ // 26 ~ 30: 预留字段给后续扩展使用
58
60
  // 31 ~ 40: 提供给开发自定义
59
61
  // --------------------------字段列表--------------------------
60
62
  return { arr, nextIndex: 31 };
@@ -105,15 +105,20 @@ function getNowString(): string {
105
105
  /**
106
106
  * 获取当前页面信息
107
107
  */
108
- function getPageInfo(): IPage | null {
108
+ function getPageInfo(): { page: IPage, pageDepth: number } {
109
109
  const pages = getCurrentPages();
110
+ let page: IPage;
111
+ let pageDepth = 1; // 页面深度
110
112
  // 首页未渲染
111
113
  if (pages.length === 0) {
112
114
  const launch = syncApi.getLaunchOptionsSync() as any;
113
- return { route: launch.path, options: launch.query };
115
+ page = { route: launch.path, options: launch.query };
116
+ } else {
117
+ pageDepth = pages.length;
118
+ // 如果最后一个为空,则当前是在插件页,继续找上一个页面
119
+ page = pages.reverse().find((t: IPage) => t);
114
120
  }
115
- // 如果最后一个为空,则当前是在插件页,继续找上一个页面
116
- return pages.reverse().find((t: IPage) => t);
121
+ return { page, pageDepth };
117
122
  }
118
123
 
119
124
  let launchOptions: string | null = null;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * 负责对象的克隆
3
+ */
4
+
5
+ const maxArrLen = 10;
6
+ const maxStrLen = 30;
7
+ const maxFieldLen = 20;
8
+
9
+ function isBasicsType(obj: any): { isBasics: boolean, value: any } {
10
+ const type = typeof obj;
11
+ if (obj === null || obj === undefined
12
+ || type === 'number' || type === 'boolean' || type === 'bigint') {
13
+ return { isBasics: true, value: obj };
14
+ }
15
+ if (type === 'string') {
16
+ const value = obj.substr(0, maxStrLen);
17
+ return { isBasics: true, value };
18
+ }
19
+ if (type === 'function' || type === 'symbol') {
20
+ return { isBasics: true, value: undefined };
21
+ }
22
+ return { isBasics: false, value: obj };
23
+ }
24
+
25
+ function isArrayType(obj: any): { isArray: boolean, value: any } {
26
+ if (Array.isArray(obj)) {
27
+ const arr = obj.slice(0, maxArrLen);
28
+ return { isArray: true, value: arr };
29
+ }
30
+ return { isArray: false, value: obj };
31
+ }
32
+
33
+ /**
34
+ * 深表克隆
35
+ * @param obj
36
+ * @param depth 当前克隆的深度
37
+ * @param maxDepth 最大克隆深度
38
+ */
39
+ function deepClone(obj: any, depth = 0, maxDepth = 5): any {
40
+ if (depth > maxDepth) {
41
+ return undefined;
42
+ }
43
+ const res1 = isBasicsType(obj);
44
+ if (res1.isBasics) {
45
+ return res1.value;
46
+ }
47
+ const res2 = isArrayType(obj);
48
+ if (res2.isArray) {
49
+ return res2.value.map((t: any) => deepClone(t, depth + 1, maxDepth));
50
+ }
51
+ const names = Object.getOwnPropertyNames(obj).slice(0, maxFieldLen);
52
+ const newObj = {};
53
+ for (const name of names) {
54
+ if (name === '__webviewId__') {
55
+ continue;
56
+ }
57
+ const value = obj[name];
58
+ const res1 = isBasicsType(value);
59
+ if (res1.isBasics) {
60
+ // @ts-ignore
61
+ newObj[name] = res1.value;
62
+ continue;
63
+ }
64
+ const res2 = isArrayType(value);
65
+ if (res2.isArray) {
66
+ // @ts-ignore
67
+ newObj[name] = res2.value.map((t: any) => deepClone(t, depth + 1, maxDepth));
68
+ continue;
69
+ }
70
+ // @ts-ignore
71
+ newObj[name] = deepClone(value, depth + 1, maxDepth);
72
+ }
73
+ return newObj;
74
+ }
75
+
76
+ /**
77
+ * 获取触发绑定事件的携带数据
78
+ * @param event
79
+ */
80
+ function getEventExtend(event: any): any {
81
+ if (!event) {
82
+ return '';
83
+ }
84
+ const { currentTarget, mark, detail } = event;
85
+ const obj = {};
86
+ // 一般把数据放在dataset或者mark中
87
+ const dataset = currentTarget?.dataset;
88
+ if (dataset && Object.keys(dataset).length) {
89
+ Object.assign(obj, { dataset });
90
+ }
91
+ if (mark && Object.keys(mark).length) {
92
+ Object.assign(obj, { mark });
93
+ }
94
+ // 如果以上都没有,那可能就是在detail中
95
+ if (detail && Object.keys(obj).length === 0) {
96
+ Object.assign(obj, { detail });
97
+ }
98
+ return deepClone(obj);
99
+ }
100
+
101
+ export default {
102
+ deepClone,
103
+ getEventExtend,
104
+ };
@@ -2,7 +2,10 @@
2
2
  * 负责组件的全埋点
3
3
  */
4
4
 
5
+ // / <reference path='./types.ts'/>
6
+
5
7
  import helper from './helper';
8
+ import clone from './clone';
6
9
 
7
10
  let originalComponent: any = null;
8
11
 
@@ -14,11 +17,13 @@ function proxyBindEvent(componentName: string, methods: any, methodName: string)
14
17
  }
15
18
  // eslint-disable-next-line no-param-reassign
16
19
  methods[methodName] = function (...args: any[]): any {
17
- const extend = helper.getEventExtend(args[0]) ; // 把触发事件附加数据也带上
20
+ const extend = clone.getEventExtend(args[0]) ; // 把触发事件附加数据也带上
18
21
  // 执行原函数之后再发埋点
19
22
  return helper.executeFunc(this, original, args, () => {
20
- const data = helper.deepClone(this.data);
21
- helper.reportData(`Component_${componentName}`, methodName, data, extend);
23
+ const data = clone.deepClone(this.data);
24
+ const eventName = `Component_${componentName}`;
25
+ helper.setLastBindEvent({ eventName, methodName, data, extend });
26
+ helper.reportData(eventName, methodName, data, extend);
22
27
  });
23
28
  };
24
29
  }
@@ -2,6 +2,8 @@
2
2
  * 自动埋点辅助类
3
3
  */
4
4
 
5
+ // / <reference path='./types.ts'/>
6
+
5
7
  let reporter: any = null;
6
8
 
7
9
  function reportData(...args: any[]): any {
@@ -15,6 +17,28 @@ function reportData(...args: any[]): any {
15
17
  reporter.report2(...args);
16
18
  }
17
19
 
20
+ let lastBindEvent: IBindEvent;
21
+
22
+ /**
23
+ * 获取最后一个触发绑定事件埋点
24
+ */
25
+ function getLastBindEvent(): IBindEvent {
26
+ return lastBindEvent;
27
+ }
28
+
29
+ /**
30
+ * 设置最后一个触发绑定事件埋点
31
+ * @param info
32
+ */
33
+ function setLastBindEvent(info: IBindEvent): void {
34
+ // 获取最后一个页面(排除插件页,插件页是null)
35
+ const pages = getCurrentPages().reverse();
36
+ const page = pages.find((t: any) => t) || {};
37
+ // eslint-disable-next-line
38
+ info.pageUrl = page.route;
39
+ lastBindEvent = info;
40
+ }
41
+
18
42
  /**
19
43
  * 空函数
20
44
  */
@@ -26,125 +50,29 @@ function emptyFunc(): void {
26
50
  * @param context
27
51
  * @param func
28
52
  * @param args
29
- * @param doneCallback
53
+ * @param callback
30
54
  */
31
- function executeFunc(context: any, func: Function, args: any[], doneCallback: Function): any {
55
+ function executeFunc(context: any, func: Function, args: any[], callback: Function): any {
32
56
  const value = func.apply(context, args);
33
57
  // 如果不会返回promise
34
58
  if (!value || !value.then || typeof value.then !== 'function') {
35
- doneCallback();
59
+ callback();
36
60
  return value;
37
61
  }
38
62
 
39
63
  return value.then((res: any) => {
40
- doneCallback();
64
+ callback();
41
65
  return res;
42
66
  }).catch((err: any) => {
43
- doneCallback();
67
+ callback();
44
68
  return Promise.reject(err);
45
69
  });
46
70
  }
47
71
 
48
- const maxArrLen = 10;
49
- const maxStrLen = 30;
50
- const maxFieldLen = 20;
51
-
52
- function isBasicsType(obj: any): { isBasics: boolean, value: any } {
53
- const type = typeof obj;
54
- if (obj === null || obj === undefined
55
- || type === 'number' || type === 'boolean' || type === 'bigint') {
56
- return { isBasics: true, value: obj };
57
- }
58
- if (type === 'string') {
59
- const value = obj.substr(0, maxStrLen);
60
- return { isBasics: true, value };
61
- }
62
- if (type === 'function' || type === 'symbol') {
63
- return { isBasics: true, value: undefined };
64
- }
65
- return { isBasics: false, value: obj };
66
- }
67
-
68
- function isArrayType(obj: any): { isArray: boolean, value: any } {
69
- if (Array.isArray(obj)) {
70
- const arr = obj.slice(0, maxArrLen);
71
- return { isArray: true, value: arr };
72
- }
73
- return { isArray: false, value: obj };
74
- }
75
-
76
- /**
77
- * 深表克隆
78
- * @param obj
79
- * @param depth 当前克隆的深度
80
- * @param maxDepth 最大克隆深度
81
- */
82
- function deepClone(obj: any, depth = 0, maxDepth = 5): 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, maxDepth));
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, maxDepth));
111
- continue;
112
- }
113
- // @ts-ignore
114
- newObj[name] = deepClone(value, depth + 1, maxDepth);
115
- }
116
- return newObj;
117
- }
118
-
119
- /**
120
- * 获取触发绑定事件的携带数据
121
- * @param event
122
- */
123
- function getEventExtend(event: any): string {
124
- if (!event) {
125
- return '';
126
- }
127
- const { currentTarget, mark, detail } = event;
128
- const obj = {};
129
- // 一般把数据放在dataset或者mark中
130
- const dataset = currentTarget?.dataset;
131
- if (dataset && Object.keys(dataset).length) {
132
- Object.assign(obj, { dataset });
133
- }
134
- if (mark && Object.keys(mark).length) {
135
- Object.assign(obj, { mark });
136
- }
137
- // 如果以上都没有,那可能就是在detail中
138
- if (detail && Object.keys(obj).length === 0) {
139
- Object.assign(obj, { detail });
140
- }
141
- return deepClone(obj);
142
- }
143
-
144
72
  export default {
145
73
  reportData,
146
74
  emptyFunc,
147
75
  executeFunc,
148
- deepClone,
149
- getEventExtend,
76
+ getLastBindEvent,
77
+ setLastBindEvent,
150
78
  };
@@ -2,7 +2,10 @@
2
2
  * 负责页面的全埋点
3
3
  */
4
4
 
5
+ // / <reference path='./types.ts'/>
6
+
5
7
  import helper from './helper';
8
+ import clone from './clone';
6
9
 
7
10
  // 必须上报的生命周期函数
8
11
  const mustLifeMethods = ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload'];
@@ -23,28 +26,42 @@ function proxyLifeMethod(pageOptions: any, methodName: string): void {
23
26
  const original = pageOptions[methodName] || helper.emptyFunc;
24
27
  // eslint-disable-next-line no-param-reassign
25
28
  pageOptions[methodName] = function (...args: any[]): any {
26
- let exposureTime = 0; // 页面曝光时长
27
- let showCount = 0; // 曝光次数
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
- exposureTime = Date.now() - this.tmsPageShowTime;
34
- this.tmsPageShowTime = 0;
35
- showCount = this.tmsPageShowCount;
36
- }
37
-
38
29
  // 生命周期函数先发埋点,避免次过程用户退出而丢失埋点
39
- const options = helper.deepClone(args[0], 0, 1);
40
- const showInfo = showCount ? { showCount, exposureTime } : null;
41
- helper.reportData(`Page_${methodName}`, options, showInfo);
30
+ const options = clone.deepClone(args[0], 0, 1);
31
+ const extend = getPageLifeExtend(this, methodName);
32
+ helper.reportData(`Page_${methodName}`, options, extend);
42
33
 
43
34
  // 执行原函数
44
35
  return helper.executeFunc(this, original, args, helper.emptyFunc);
45
36
  };
46
37
  }
47
38
 
39
+ // 获取生命周期函数上报时的附加信息
40
+ function getPageLifeExtend(page: any, methodName: string): IPageLifeExtend {
41
+ /* eslint-disable */
42
+ page.tmsPageShowCount = page.tmsPageShowCount || 0;
43
+ // 页面生命周期内第几次曝光
44
+ let showCount: number;
45
+ // 距离onShow的时长,如果是onShow函数则为距离onLoad的时长
46
+ let lessShowTime: number;
47
+ if (methodName === 'onShow') {
48
+ page.tmsPageShowCount += 1;
49
+ page.tmsPageShowTime = Date.now();
50
+ showCount = page.tmsPageShowCount;
51
+ lessShowTime = page.tmsPageShowTime - page.tmsPageLoadTime;
52
+ } else if (methodName === 'onLoad') {
53
+ showCount = 1;
54
+ lessShowTime = 0;
55
+ page.tmsPageLoadTime = Date.now();
56
+ } else {
57
+ showCount = page.tmsPageShowCount;
58
+ lessShowTime = Date.now() - page.tmsPageShowTime;
59
+ }
60
+ /* eslint-enable */
61
+ const lastBindEvent = helper.getLastBindEvent();
62
+ return { showCount, lessShowTime, lastBindEvent };
63
+ }
64
+
48
65
  // 劫持绑定事件
49
66
  function proxyBindEvent(pageOptions: any, methodName: string): void {
50
67
  const original = pageOptions[methodName];
@@ -53,17 +70,19 @@ function proxyBindEvent(pageOptions: any, methodName: string): void {
53
70
  }
54
71
  // eslint-disable-next-line no-param-reassign
55
72
  pageOptions[methodName] = function (...args: any[]): any {
56
- const extend = helper.getEventExtend(args[0]) ; // 把触发事件附加数据也带上
73
+ const extend = clone.getEventExtend(args[0]) ; // 把触发事件附加数据也带上
57
74
  // 执行原函数之后再发埋点
58
75
  return helper.executeFunc(this, original, args, () => {
59
- const data = helper.deepClone(this.data);
60
- helper.reportData(`Page_${methodName}`, data, extend);
76
+ const data = clone.deepClone(this.data);
77
+ const eventName = `Page_${methodName}`;
78
+ helper.setLastBindEvent({ eventName, methodName, data, extend });
79
+ helper.reportData(eventName, data, extend);
61
80
  });
62
81
  };
63
82
  }
64
83
 
65
84
  // 劫持Page
66
- function init(): void {
85
+ function proxyPage(): void {
67
86
  // @ts-ignore
68
87
  originalPage = Page;
69
88
  // @ts-ignore
@@ -82,6 +101,33 @@ function init(): void {
82
101
  };
83
102
  }
84
103
 
104
+ // 劫持导航api
105
+ function proxyNavigateApi(api: string): void {
106
+ // @ts-ignore
107
+ const owx = wx;
108
+ const originalApi = owx[api];
109
+ Object.defineProperty(owx, api, {
110
+ writable: true,
111
+ enumerable: true,
112
+ configurable: true,
113
+ value(...args: any) {
114
+ originalApi.apply(this, args);
115
+
116
+ const { url = '' } = args[0] || {};
117
+ const [path, params = ''] = url.split('?');
118
+ helper.reportData('wx_navigate_before', api, path, params);
119
+ },
120
+ });
121
+ }
122
+
123
+ // 劫持Page
124
+ function init(): void {
125
+ proxyPage();
126
+ proxyNavigateApi('navigateTo');
127
+ proxyNavigateApi('reLaunch');
128
+ proxyNavigateApi('redirectTo');
129
+ }
130
+
85
131
  export default {
86
132
  init,
87
133
  };
@@ -0,0 +1,45 @@
1
+ /* eslint-disable */
2
+
3
+ /**
4
+ * 绑定的触发事件埋点
5
+ */
6
+ interface IBindEvent {
7
+ /**
8
+ * 触发事件的页面url
9
+ */
10
+ pageUrl?: string,
11
+ /**
12
+ * 事件名,如 `Page_${methodName}`、`Component_${componentName}`
13
+ */
14
+ eventName: string,
15
+ /**
16
+ * 触发的函数名
17
+ */
18
+ methodName: string,
19
+ /**
20
+ * Page或者Component的data
21
+ */
22
+ data: object,
23
+ /**
24
+ * 触发事件的携带参数
25
+ */
26
+ extend: object,
27
+ }
28
+
29
+ /**
30
+ * 页面生命周期函数的埋点附加数据
31
+ */
32
+ interface IPageLifeExtend {
33
+ /**
34
+ * 页面生命周期内第几次曝光
35
+ */
36
+ showCount: number,
37
+ /**
38
+ * 距离onShow的时长,如果是onShow函数则为距离onLoad的时长
39
+ */
40
+ lessShowTime?: number,
41
+ /**
42
+ * 最后一个触发绑定事件埋点
43
+ */
44
+ lastBindEvent: IBindEvent | null,
45
+ }