spy-client 2.0.7 → 2.1.1
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/README.md +119 -10
- package/dist/head/base.d.ts +1 -1
- package/dist/lib/huffman.d.ts +9 -0
- package/dist/lib/interface.d.ts +59 -1
- package/dist/lib/util.d.ts +22 -0
- package/dist/module/longtask.d.ts +1 -1
- package/dist/module/resource.d.ts +19 -9
- package/dist/module/tti.d.ts +1 -1
- package/dist/spy-client-basic.esm.js +233 -1167
- package/dist/spy-client-basic.iife.js +237 -1171
- package/dist/spy-client-basic.iife.min.js +1 -1
- package/dist/spy-client-basic.js +238 -1172
- package/dist/spy-client-basic.min.js +1 -1
- package/dist/spy-client-basic.mjs +82 -54
- package/dist/spy-client.d.ts +5 -4
- package/dist/spy-client.esm.js +1393 -5662
- package/dist/spy-client.iife.js +1401 -5670
- package/dist/spy-client.iife.min.js +1 -1
- package/dist/spy-client.js +1402 -5671
- package/dist/spy-client.min.js +1 -1
- package/dist/spy-client.mjs +704 -455
- package/dist/spy-head.js +330 -276
- package/dist/spy-head.min.js +1 -1
- package/dist/spy-local-cache.d.ts +18 -0
- package/dist/spy-local-cache.js +690 -0
- package/dist/spy-local-cache.min.js +1 -0
- package/example/index.html +8 -2
- package/example/local-cache.html +97 -0
- package/example/saveCache.js +41 -0
- package/karma.conf.js +111 -0
- package/package.json +21 -20
- package/src/head/error.ts +14 -15
- package/src/head/whitescreen.ts +8 -5
- package/src/lib/huffman.ts +326 -0
- package/src/lib/interface.ts +70 -1
- package/src/lib/util.ts +101 -0
- package/src/module/resource.ts +226 -129
- package/src/spy-client-basic.ts +8 -7
- package/src/spy-client.ts +15 -6
- package/src/spy-local-cache.ts +385 -0
- package/src/types/globals.d.ts +1 -1
package/src/head/whitescreen.ts
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
SpyHeadConf,
|
|
9
9
|
WhiteScreenErrorConf,
|
|
10
10
|
} from '../lib/spyHeadInterface';
|
|
11
|
+
import {getResTiming} from '../lib/util';
|
|
12
|
+
|
|
11
13
|
import spyHead from './base';
|
|
12
14
|
|
|
13
15
|
export function init(conf: SpyHeadConf) {
|
|
@@ -23,11 +25,12 @@ export function init(conf: SpyHeadConf) {
|
|
|
23
25
|
if (!window.performance) {
|
|
24
26
|
return false;
|
|
25
27
|
}
|
|
26
|
-
const pf = window.performance.timing;
|
|
27
|
-
const netStr = `&
|
|
28
|
-
+ `&
|
|
29
|
-
+ `&
|
|
30
|
-
+ `&
|
|
28
|
+
const pf = getResTiming(window.performance.timing);
|
|
29
|
+
const netStr = `&wait=${pf.wait}`
|
|
30
|
+
+ `&dns=${pf.dns}`
|
|
31
|
+
+ `&connect=${pf.connect}`
|
|
32
|
+
+ `&requestTime=${pf.req}`
|
|
33
|
+
+ `&resoneTime=${pf.res}`;
|
|
31
34
|
return netStr;
|
|
32
35
|
}
|
|
33
36
|
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Huffman
|
|
3
|
+
* @author kaivean
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
赫夫曼编码
|
|
9
|
+
基本介绍
|
|
10
|
+
|
|
11
|
+
1.赫夫曼编码也翻译为 哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式, 属于一种程序算法
|
|
12
|
+
赫夫曼编码是赫哈夫曼树在电讯通信中的经典的应用之一。
|
|
13
|
+
|
|
14
|
+
2.赫夫曼编码广泛地用于数据文件压缩。其压缩率通常在20%~90%之间
|
|
15
|
+
赫夫曼码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,称之为最佳编码
|
|
16
|
+
|
|
17
|
+
*
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
class HuffmanNode {
|
|
22
|
+
data;// 存放数据(字符本身),比如 'a' => 97, ' '=>32
|
|
23
|
+
weight;// 权值,表示字符串出现的次数
|
|
24
|
+
left: HuffmanNode;
|
|
25
|
+
right: HuffmanNode;
|
|
26
|
+
constructor(data: any, weight: number) {
|
|
27
|
+
this.data = data;
|
|
28
|
+
this.weight = weight;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 前序遍历
|
|
32
|
+
preOrder(arr: any) {
|
|
33
|
+
arr.push(this);
|
|
34
|
+
if (this.left) {
|
|
35
|
+
this.left.preOrder(arr);
|
|
36
|
+
}
|
|
37
|
+
if (this.right) {
|
|
38
|
+
this.right.preOrder(arr);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
*
|
|
46
|
+
* @param {接受字符数组} bytes
|
|
47
|
+
* @return 返回的就是list形式
|
|
48
|
+
*/
|
|
49
|
+
function getNodes(bytes: Array<any>): HuffmanNode[] {
|
|
50
|
+
//创建一个list
|
|
51
|
+
let list: HuffmanNode[] = [];
|
|
52
|
+
//counts 统计每一个byte出现的次数
|
|
53
|
+
let counts: any = {};
|
|
54
|
+
for (let b of bytes) {
|
|
55
|
+
let count = counts[b]; // map还没有这个字符数据
|
|
56
|
+
if (count == null) {
|
|
57
|
+
counts[b] = 1;
|
|
58
|
+
} else {
|
|
59
|
+
counts[b]++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const [key, value] of Object.entries(counts)) {
|
|
64
|
+
list.push(new HuffmanNode(key, value as number));
|
|
65
|
+
}
|
|
66
|
+
return list;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
// 通过list创建赫夫曼树
|
|
71
|
+
function createHuffmanTree(nodes: HuffmanNode[]): HuffmanNode | undefined {
|
|
72
|
+
const compareFun = function (a: HuffmanNode, b: HuffmanNode) {
|
|
73
|
+
return a.weight - b.weight;
|
|
74
|
+
};
|
|
75
|
+
while (nodes.length > 1) {
|
|
76
|
+
// 排序,从小到大
|
|
77
|
+
nodes.sort(compareFun);
|
|
78
|
+
// 取出第一颗最小的二叉树
|
|
79
|
+
let leftNode = nodes.shift();
|
|
80
|
+
let rightNode = nodes.shift();
|
|
81
|
+
if (leftNode && rightNode) {
|
|
82
|
+
// 创建一个新的二叉树,它的根节点,没有data,只有权值
|
|
83
|
+
let parent = new HuffmanNode(null, leftNode.weight + rightNode.weight);
|
|
84
|
+
parent.left = leftNode;
|
|
85
|
+
parent.right = rightNode;
|
|
86
|
+
|
|
87
|
+
//将新的二叉树,加入到nodes
|
|
88
|
+
nodes.unshift(parent);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
// nodes最后的节点,就是根节点
|
|
93
|
+
return nodes.shift();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 生成赫夫曼树对应的赫夫曼编码表
|
|
97
|
+
function getHuffmanCodes(root: any) {
|
|
98
|
+
if (root == null) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// 生成赫夫曼树对应的赫夫曼编码表
|
|
102
|
+
// 思路
|
|
103
|
+
// 1.将赫夫曼编码表存放在map里面
|
|
104
|
+
// 2.在生成赫夫曼编码表时,需要拼接路径,定义一个数组string,存储某个叶子节点的路径
|
|
105
|
+
|
|
106
|
+
let huffmanCodes: any = {};
|
|
107
|
+
let strings: any[] = [];
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 将传入的node节点的所有叶子节点的赫夫曼编码得到,并放入到huffmanCodes集合中
|
|
111
|
+
* @param {传入节点} node
|
|
112
|
+
* @param {路径:左子节点是0,右子节点是1} code
|
|
113
|
+
* @param {用于拼接路径} string
|
|
114
|
+
*/
|
|
115
|
+
function getCodes(node: HuffmanNode, code: string, strs: string[]) {
|
|
116
|
+
let string2 = ([] as string[]).concat(strs);
|
|
117
|
+
// 将code加入到string中
|
|
118
|
+
string2.push(code);
|
|
119
|
+
if (node != null) { // 如果node == null不处理
|
|
120
|
+
// 判断当前node是叶子节点还是非叶子节点
|
|
121
|
+
if (node.data == null) {//非叶子节点
|
|
122
|
+
// 递归处理
|
|
123
|
+
// 向左递归
|
|
124
|
+
getCodes(node.left, '0', string2);
|
|
125
|
+
// 向右递归
|
|
126
|
+
getCodes(node.right, '1', string2)
|
|
127
|
+
} else {//说明是一个叶子节点
|
|
128
|
+
// 就表示找到了某个叶子节点的最后
|
|
129
|
+
huffmanCodes[node.data] = string2.join('');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
getCodes(root, "", strings);
|
|
134
|
+
return huffmanCodes;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//编写一个方法,将字符串对应的bytes数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte数组
|
|
138
|
+
/**
|
|
139
|
+
*
|
|
140
|
+
* @param {原始的字符串对应的bytes数组} bytes
|
|
141
|
+
* @param {生成的赫夫曼编码表} huffmanCodes
|
|
142
|
+
* @return 返回的是字符串对应的一个byte数组
|
|
143
|
+
*/
|
|
144
|
+
function zip(bytes: Array<number>, huffmanCodes: any) {
|
|
145
|
+
//1.利用huffmanCodes将bytes转成赫夫曼编码对应的字符串
|
|
146
|
+
let string = [];
|
|
147
|
+
//遍历数组
|
|
148
|
+
for (let b of bytes) {
|
|
149
|
+
string.push(huffmanCodes[b]);
|
|
150
|
+
}
|
|
151
|
+
return string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function huffStringToByte(strs: Array<string>) {
|
|
155
|
+
//计算赫夫曼编码字符串的长度
|
|
156
|
+
let str = strs.join('');
|
|
157
|
+
let len = Math.ceil(str.length / 8);
|
|
158
|
+
//创建存储压缩后的byte数组
|
|
159
|
+
let huffmanCodeByte = new Array(len + 1);
|
|
160
|
+
let index = 0;
|
|
161
|
+
let strByte: string = ''; // 记录是第几个byte
|
|
162
|
+
for (let i = 0; i < str.length; i += 8) {
|
|
163
|
+
strByte = str.substring(i, i + 8);
|
|
164
|
+
// 将strByte转成一个byte,放入huffmanCodeByte
|
|
165
|
+
huffmanCodeByte[index] = parseInt(strByte, 2);
|
|
166
|
+
index++;
|
|
167
|
+
}
|
|
168
|
+
// 记录最后一位二进制码的长度,因为,比如最后一位二进制strByte为00101时,
|
|
169
|
+
// parseInt(strByte, 2)后等于5,前面的两个00已经丢失,所以必须记录长度,以便解码时补足前面的0
|
|
170
|
+
huffmanCodeByte[index] = strByte.length;
|
|
171
|
+
return huffmanCodeByte;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 使用一个方法,封装前面的方法,便于调用
|
|
175
|
+
/**
|
|
176
|
+
*
|
|
177
|
+
* @param {原始的字符串对应的字节数组} bytes
|
|
178
|
+
* @returns 是经过赫夫曼编码处理后,压缩后的字节数组
|
|
179
|
+
*
|
|
180
|
+
*/
|
|
181
|
+
function huffmanZip(bytes: Array<any>) {
|
|
182
|
+
// 1.生成节点数组
|
|
183
|
+
let nodes = getNodes(bytes);
|
|
184
|
+
// 2.根据节点数组创建赫夫曼树
|
|
185
|
+
let root = createHuffmanTree(nodes);
|
|
186
|
+
// 3.根据赫夫曼树生成赫夫曼编码
|
|
187
|
+
let hufumanCodes = getHuffmanCodes(root);
|
|
188
|
+
// 4.根据生成的赫夫曼编码生成压缩后的赫夫曼编码字节数组
|
|
189
|
+
let hufumanStrArr = zip(bytes, hufumanCodes);
|
|
190
|
+
let hufumanByteArr = huffStringToByte(hufumanStrArr);
|
|
191
|
+
|
|
192
|
+
return {result: hufumanByteArr, codes: hufumanCodes};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 完成数据的解压
|
|
196
|
+
// 思路
|
|
197
|
+
// 1.将huffmanBytesArr先转成赫夫曼编码对应的二进制字符串
|
|
198
|
+
// 2.将赫夫曼编码对应的二进制的字符串转成赫夫曼编码字符串
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
*
|
|
202
|
+
* @param {表示是否需要补高位,如果是true,表示需要,如果是false,表示不需要,如果是最后一个字节不需要补高位} flag
|
|
203
|
+
* @param {传入的byte} byte
|
|
204
|
+
* @returns 是byte对应的二进制字符串
|
|
205
|
+
*/
|
|
206
|
+
function huffByteToString(flag: boolean, byte: number) {
|
|
207
|
+
//如果是
|
|
208
|
+
if (flag) {
|
|
209
|
+
byte |= 256;
|
|
210
|
+
}
|
|
211
|
+
let str = Number(byte).toString(2)
|
|
212
|
+
if (flag) {
|
|
213
|
+
return str.substring(str.length - 8);
|
|
214
|
+
} else {
|
|
215
|
+
return str;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
//编写一份方法,完成对压缩数据的解码
|
|
220
|
+
/**
|
|
221
|
+
*
|
|
222
|
+
* @param {赫夫曼编码表} huffmanCodes
|
|
223
|
+
* @param {赫夫曼编码得到的二进制数组} huffmanBytes
|
|
224
|
+
*/
|
|
225
|
+
function decode(huffmanCodes: {[key:string]: string}, huffmanBytes: Array<number>) {
|
|
226
|
+
// 1.先得到二进制字符串 形式11001111111011......
|
|
227
|
+
let heffmanStrArr = [];
|
|
228
|
+
for (let i = 0; i < huffmanBytes.length - 1; i++) {
|
|
229
|
+
//判断是不是最后一个字节
|
|
230
|
+
let flag = (i !== huffmanBytes.length - 2);
|
|
231
|
+
heffmanStrArr.push(huffByteToString(flag, huffmanBytes[i]))
|
|
232
|
+
}
|
|
233
|
+
// 最后一位记录的是最后一位二进制字符串的长度,该长度主要用于补足最后一位丢失的0,所以要单独处理,
|
|
234
|
+
let lastByteStr = heffmanStrArr[heffmanStrArr.length - 1];
|
|
235
|
+
let lastByteLength = huffmanBytes[huffmanBytes.length - 1];
|
|
236
|
+
lastByteStr = '00000000'.substring(8 - (lastByteLength - lastByteStr.length)) + lastByteStr;
|
|
237
|
+
heffmanStrArr[heffmanStrArr.length - 1] = lastByteStr;
|
|
238
|
+
|
|
239
|
+
// 把赫夫曼编码表进行调换
|
|
240
|
+
let map: {[key:string]: string} = {};
|
|
241
|
+
for (const [key, value] of Object.entries(huffmanCodes)) {
|
|
242
|
+
map[value] = key;
|
|
243
|
+
}
|
|
244
|
+
let heffmanStr = heffmanStrArr.join('');
|
|
245
|
+
let list = [];
|
|
246
|
+
let len = heffmanStr.length;
|
|
247
|
+
for (let i = 0; i < len;) {
|
|
248
|
+
let count = 1;
|
|
249
|
+
let flag = true;
|
|
250
|
+
let b: string | null = null;
|
|
251
|
+
while (flag && i + count <= len) {
|
|
252
|
+
// 取出一个1或0
|
|
253
|
+
// i不动,count移动,直到匹配到一个字符
|
|
254
|
+
let key = heffmanStr.substring(i, i + count);
|
|
255
|
+
if (key === '') {
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
b = map[key];
|
|
259
|
+
if (!b) {//没有匹配到
|
|
260
|
+
count++;
|
|
261
|
+
} else {
|
|
262
|
+
//匹配到
|
|
263
|
+
flag = false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
list.push(parseInt(b as string));
|
|
267
|
+
i += count;
|
|
268
|
+
}
|
|
269
|
+
// 当for循环结束后,list中就存放了所有的字符
|
|
270
|
+
|
|
271
|
+
return list;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
// js byte[] 和string 相互转换 UTF-8
|
|
276
|
+
function stringToByte(str: string): Array<number> {
|
|
277
|
+
let bytes = [];
|
|
278
|
+
for (let index = 0; index < str.length; index++) {
|
|
279
|
+
bytes.push(str.charCodeAt(index));
|
|
280
|
+
}
|
|
281
|
+
return bytes;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function byteToString(arr: Array<number>): string {
|
|
285
|
+
let data = '';
|
|
286
|
+
for (const code of arr) {
|
|
287
|
+
data += String.fromCharCode(code);
|
|
288
|
+
}
|
|
289
|
+
return data;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function huffmanEncode(str: string) {
|
|
293
|
+
let bytes = stringToByte(str);
|
|
294
|
+
let {result, codes} = huffmanZip(bytes);
|
|
295
|
+
return {
|
|
296
|
+
codes,
|
|
297
|
+
result: byteToString(result)
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function huffmanDecode(codes: any, str: string) {
|
|
302
|
+
let bytes = stringToByte(str);
|
|
303
|
+
const data = decode(codes, bytes);
|
|
304
|
+
return byteToString(data);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// For test
|
|
308
|
+
// let content = JSON.stringify({
|
|
309
|
+
// type: 3,
|
|
310
|
+
// fm: 'disp',
|
|
311
|
+
// data: [{"base":{"size":{"doc":{"w":360,"h":4875},"wind":{"w":360,"h":640},"scr":{"w":360,"h":640}},"vsb":"visible","num":16},"t":1629773746698,"path":"/s"}],
|
|
312
|
+
// qid: 10991431029479106376,
|
|
313
|
+
// did: '8dd09c47c7bc90c9fd7274f0ad2c581e',
|
|
314
|
+
// q: '刘德华',
|
|
315
|
+
// t: 1629773746698
|
|
316
|
+
// });
|
|
317
|
+
|
|
318
|
+
// console.log('压缩前的字符串', content, '其长度:', content.length);
|
|
319
|
+
|
|
320
|
+
// const res = huffmanEncode(content);
|
|
321
|
+
// console.log('压缩后的字符串长度', res.result.length);
|
|
322
|
+
// console.log('压缩后的字符串', res.result);
|
|
323
|
+
|
|
324
|
+
// const out = huffmanDecode(res.codes, res.result);
|
|
325
|
+
// console.log('解压后的字符串', out, '其长度:', out.length);
|
|
326
|
+
// console.log('解压后的数据', JSON.stringify(JSON.parse(out)));
|
package/src/lib/interface.ts
CHANGED
|
@@ -86,12 +86,26 @@ export interface ResourceMetric {
|
|
|
86
86
|
imgTransferSize: number;
|
|
87
87
|
fontTransferSize: number;
|
|
88
88
|
|
|
89
|
+
jsDuration: number;
|
|
90
|
+
cssDuration: number;
|
|
91
|
+
imgDuration: number;
|
|
92
|
+
fontDuration: number;
|
|
93
|
+
|
|
89
94
|
// js cache率
|
|
90
95
|
jsCacheRate: number;
|
|
91
96
|
cssCacheRate: number;
|
|
92
97
|
imgCacheRate: number;
|
|
93
98
|
};
|
|
94
|
-
export
|
|
99
|
+
export interface ResourceHostMetric {
|
|
100
|
+
[host: string]: {
|
|
101
|
+
hostNum: number;
|
|
102
|
+
hostSize: number;
|
|
103
|
+
hostTransferSize: number;
|
|
104
|
+
hostDuration: number;
|
|
105
|
+
hostCacheRate: number;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
export type ResourceCB = (metric: ResourceMetric, hostMetric: ResourceHostMetric) => void;
|
|
95
109
|
|
|
96
110
|
|
|
97
111
|
export interface ResourceErrorInfo {
|
|
@@ -99,6 +113,12 @@ export interface ResourceErrorInfo {
|
|
|
99
113
|
msg: string;
|
|
100
114
|
// 发生异常的资源元素的xpath信息,一直到body
|
|
101
115
|
xpath: string;
|
|
116
|
+
// 资源host
|
|
117
|
+
host: string;
|
|
118
|
+
// 资源类型
|
|
119
|
+
type: string;
|
|
120
|
+
// 资源耗时
|
|
121
|
+
dur?: number;
|
|
102
122
|
}
|
|
103
123
|
export type ResourceErrorCB = (info: ResourceErrorInfo) => void;
|
|
104
124
|
|
|
@@ -255,3 +275,52 @@ export interface PageLongtaskMetric {
|
|
|
255
275
|
pageIframeLongtaskNum: number;
|
|
256
276
|
}
|
|
257
277
|
export type PageLongtaskCB = (metric: PageLongtaskMetric) => void;
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
export interface ResOption {
|
|
281
|
+
ignorePaths?: string[];
|
|
282
|
+
trigger?: 'load' | 'leave';
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export interface BigImgOption {
|
|
286
|
+
/**
|
|
287
|
+
* 体积大于该阈值,就认为是大图,默认150,单位是kb
|
|
288
|
+
*/
|
|
289
|
+
maxSize?: number;
|
|
290
|
+
/**
|
|
291
|
+
* 忽略指定path的资源
|
|
292
|
+
*/
|
|
293
|
+
ignorePaths?: string[];
|
|
294
|
+
/**
|
|
295
|
+
* 触发时机,在load事件触发后,还是在用户离开页面后,收集出现的加载慢的资源。,默认是load
|
|
296
|
+
*/
|
|
297
|
+
trigger?: 'load' | 'leave';
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export interface HttpResOption {
|
|
301
|
+
/**
|
|
302
|
+
* 忽略指定path的资源
|
|
303
|
+
*/
|
|
304
|
+
ignorePaths?: string[];
|
|
305
|
+
/**
|
|
306
|
+
* 触发时机,在load事件触发后,还是在用户离开页面后,收集出现的加载慢的资源。,默认是load
|
|
307
|
+
*/
|
|
308
|
+
trigger?: 'load' | 'leave';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface SlowOption {
|
|
312
|
+
/**
|
|
313
|
+
* 加载时长大于该阈值,就认为是慢资源,默认1000,单位是ms
|
|
314
|
+
*/
|
|
315
|
+
threshold?: number;
|
|
316
|
+
/**
|
|
317
|
+
* 忽略指定path的资源
|
|
318
|
+
*/
|
|
319
|
+
ignorePaths?: string[];
|
|
320
|
+
/**
|
|
321
|
+
* 触发时机,在load事件触发后,还是在用户离开页面后,收集出现的加载慢的资源。,默认是load
|
|
322
|
+
*/
|
|
323
|
+
trigger?: 'load' | 'leave';
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
|
package/src/lib/util.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file utils
|
|
3
|
+
* @author kaivean
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function assign(...args: any[]) {
|
|
7
|
+
const __assign = Object.assign || function __assign(t: any) {
|
|
8
|
+
for (let s, i = 1, n = arguments.length; i < n; i++) {
|
|
9
|
+
// eslint-disable-next-line prefer-rest-params
|
|
10
|
+
s = arguments[i];
|
|
11
|
+
for (const p in s) {
|
|
12
|
+
if (Object.prototype.hasOwnProperty.call(s, p)) {
|
|
13
|
+
t[p] = s[p];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return t;
|
|
18
|
+
};
|
|
19
|
+
return __assign.apply(this, args);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface URLINFO {
|
|
23
|
+
protocol: string;
|
|
24
|
+
host: string;
|
|
25
|
+
pathname: string;
|
|
26
|
+
ext: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function getUrlInfoFromURL(url: string): URLINFO | undefined {
|
|
30
|
+
if (URL) {
|
|
31
|
+
const obj = new URL(url);
|
|
32
|
+
if (obj.host !== undefined) {
|
|
33
|
+
return {
|
|
34
|
+
protocol: obj.protocol,
|
|
35
|
+
host: obj.host,
|
|
36
|
+
pathname: obj.pathname,
|
|
37
|
+
ext: '',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getUrlInfo(url: string): URLINFO {
|
|
45
|
+
let info = getUrlInfoFromURL(url);
|
|
46
|
+
|
|
47
|
+
if (!info) {
|
|
48
|
+
const parser = document.createElement('a');
|
|
49
|
+
parser.href = url;
|
|
50
|
+
info = {
|
|
51
|
+
protocol: parser.protocol,
|
|
52
|
+
host: parser.host || location.host,
|
|
53
|
+
pathname: parser.pathname,
|
|
54
|
+
ext: '',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const split = info.pathname.split('.');
|
|
59
|
+
info.ext = split[split.length - 1];
|
|
60
|
+
return info;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function f(n: number): number {
|
|
64
|
+
return +n.toFixed(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getResTiming(t: PerformanceTiming | PerformanceResourceTiming) {
|
|
68
|
+
return {
|
|
69
|
+
wait: f(t.domainLookupStart - ((t as any).navigationStart || t.fetchStart || (t as any).startTime)),
|
|
70
|
+
dns: f(t.domainLookupEnd - t.domainLookupStart),
|
|
71
|
+
connect: f(t.connectEnd - t.connectStart),
|
|
72
|
+
req: f(t.responseStart - t.requestStart),
|
|
73
|
+
res: f(t.responseEnd - t.responseStart),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
export function getxpath(el: Element | null) {
|
|
79
|
+
if (!el) {
|
|
80
|
+
return {xpath: ''};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const xpath = [];
|
|
84
|
+
while (el && el.nodeType === 1 && el !== el.parentNode) {
|
|
85
|
+
let t = el.tagName.toLowerCase();
|
|
86
|
+
if (el.getAttribute('id')) {
|
|
87
|
+
t += '[#' + el.getAttribute('id') + ']';
|
|
88
|
+
}
|
|
89
|
+
else if (el.classList && el.classList.length) {
|
|
90
|
+
t += '[.' + el.classList[el.classList.length - 1] + ']';
|
|
91
|
+
}
|
|
92
|
+
xpath.push(t);
|
|
93
|
+
if (el === document.body) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
el = el.parentNode as HTMLElement; // 修复缺陷检查
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
xpath: xpath.join('<'),
|
|
100
|
+
};
|
|
101
|
+
}
|