Posts 前端监控工具制作
Post
Cancel

前端监控工具制作

  • 原生监控
  • lighthouse api

公司项目多起来之后, 文档的作用虽然大, 但是起不到强制约束的作用, 例如部分项目没有正确使用 CDN 等。这时候我们可以利用 lighthouse 提供的 api 制作自己公司的校验军规。

原生监控

主要利用了 Window.performance 的 API。

01.png

页面性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Perf {
  init(cb: Function) {
    window.addEventListener(
      "load",
      () => {
        this.check(cb);
      },
      false
    );
  }

  check(cb: Function) {
    let timer;

    if (performance.timing.loadEventEnd) {
      clearTimeout(timer);
      this.load(cb);
    } else {
      timer = setTimeout(() => {
        this.check(cb);
      }, 100);
    }
  }

  load(cb: Function) {
    const performanceTiming = performance.timing;
    const performanceTimingData = this.processPerformance(performanceTiming);
    cb(performanceTimingData);
  }

  processPerformance(p: PerformanceTiming) {
    return {
      prevPage: p.fetchStart - p.navigationStart, // 上一个页面到这个页面的时长
      redirect: p.redirectEnd - p.redirectStart, // 重定向的时长
      dns: p.domainLookupEnd - p.connectStart, // dns 解析的时长
      connect: p.connectEnd - p.connectStart, // tcp 连接的时长
      send: p.responseEnd - p.requestStart, // 响应结束到请求结束
      ttfb: p.responseStart - p.navigationStart, // 首字节接收到的时长
      domReady: p.domInteractive - p.domLoading, // DOM 准备时长
      whiteScreen: p.domLoading - p.navigationStart, // 白屏时长
      domParse: p.domComplete - p.domLoading, // DOM 解析时长
      load: p.loadEventEnd - p.loadEventStart, // onload 的执行时间
      total: p.loadEventEnd - p.navigationStart, // 总的加载时长
    };
  }

  formatObj(data) {
    const arr = [];
    for (let key in data) {
      arr.push(`${key}=${data[key]}`);
    }
    return arr.join("&"); // {a: 1, b: 2} => a=1&b=2
  }
}

const perf = new Perf();
perf.init((data) => {
  // 图片也是请求,可以被服务器记录,并且不存在跨域问题
  new Image().src = "/pic.gif?" + perf.formatObj(data);
  console.log(data);
});

静态资源加载情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Resource {
  init(cb: Function) {
    window.onload = () => {
      let resources = performance.getEntries();
      let data = resources.map((resource) => this.processData(resource));
      cb(data);
    };
  }

  processData(data) {
    return {
      name: data.name,
      initiatorType: data.initiatorType,
      duration: data.duration,
    };
  }
}

const resource = new Resource();
resource.init((data) => {
  console.log(data);
});

ajax 监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class NetworkMonitoring {
  xhr(cb: Function) {
    const Xhr = XMLHttpRequest;

    const oldOpen = Xhr.prototype.open;
    Xhr.prototype.open = function (method, url) {
      this.info = {
        method,
        url,
      };

      return oldOpen.apply(this, arguments);
    };

    const oldSend = Xhr.prototype.send;
    Xhr.prototype.send = function (body) {
      const start = Date.now();
      const loadedHandler = (type) => () => {
        this.info.time = Date.now() - start;
        this.inifo.requestSize = body.length;
        this.info.responseSize = this.responseText.length;
        this.info.type = type;

        cb(this.info);
      };

      this.addEventListener("load", loadedHandler("load"), false);
      this.addEventListener("error", loadedHandler("error"), false);
      this.addEventListener("abort", loadedHandler("abort"), false);

      return oldSend.apply(this, arguments);
    };
  }

  fetch(cb: Function) {
    // override fetch...
  }
}

页面错误捕捉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class ErrorHandler {
  init(cb: Function) {
    // 前四个参数没有用,因为不准
    window.onerror = function (message, source, lineno, colno, error) {
      let info: { [key: string]: any } = {
        message: error.message,
        name: error.name,
      };

      let stack = error.stack;
      const matchUrl = stack.match(/:\/\/.+\d/g)[0];
      console.log("matchUrl", matchUrl); // ://localhost:1234/ts.f2f44312.js:136:13
      const matchFile = matchUrl.match(/:\/\/.+\.js/)[0]; // ://localhost:1234/ts.f2f44312.js
      console.log(matchFile);
      const [, row, column] = matchUrl.match(/:(\d+):(\d+)/);
      info.fileName = matchFile;
      info.row = row;
      info.column = column;
      cb(info);
    };
  }
}

const errorHandler = new ErrorHandler();
errorHandler.init((info) => {
  console.log(info);
});

setTimeout(() => {
  function test() {
    const x = xx;
  }

  test();
}, 1000);

lighthouse api

https://github.com/GoogleChrome/lighthouse

1
2
3
npm install -g lighthouse

lighthouse https://airhorner.com/

可以拿到网页版 lighthouse 的所有信息,根据公司需求来定制监控内容,每次上线前跑一次脚本,不符合规则的内容可以提早检测出来。

This post is licensed under CC BY 4.0 by the author.
Trending Tags
Contents

Trending Tags