Post

前端性能监控 SDK 搭建

前端性能监控 SDK 搭建

核心目标

实现:

  1. 监控性能指标和资源加载。
  2. 捕获错误。
  3. 上报数据。

1:初始化项目

1
2
3
4
mkdir performance-sdk
cd performance-sdk
npm init -y
npm install rollup --save-dev

2:设计 SDK 结构

1
2
3
4
5
6
7
8
9
performance-sdk/
├── src/
│   ├── index.js       # 主入口
│   ├── performance.js # 性能监控模块
│   ├── resource.js    # 资源监控模块
│   ├── error.js       # 错误监控模块
│   └── report.js      # 数据上报模块
├── rollup.config.js
└── package.json

3:实现性能监控

src/performance.js 中,使用 Performance API 和 Web Vitals 指标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export class PerformanceMonitor {
  constructor() {
    this.metrics = {};
    this.init();
  }

  init() {
    // 指标
    window.addEventListener('load', () => {
      const timing = performance.timing;
      this.metrics = {
        dns: timing.domainLookupEnd - timing.domainLookupStart,
        tcp: timing.connectEnd - timing.connectStart,
        request: timing.responseEnd - timing.requestStart,
        dom: timing.domContentLoadedEventEnd - timing.domLoading,
        load: timing.loadEventEnd - timing.navigationStart
      };
    });
  }

  getMetrics() {
    return this.metrics;
  }
}

4:监控资源加载

src/resource.js 中:

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
export class ResourceMonitor {
  constructor() {
    this.resources = [];
    this.init();
  }

  init() {
    const observer = new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        if (entry.initiatorType === 'script' || entry.initiatorType === 'css') {
          this.resources.push({
            name: entry.name,
            duration: entry.duration,
            type: entry.initiatorType
          });
        }
      });
    });
    observer.observe({ entryTypes: ['resource'] });
  }

  getResources() {
    return this.resources;
  }
}

5:捕获错误

src/error.js 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export class ErrorMonitor {
  constructor() {
    this.errors = [];
    this.init();
  }

  init() {
    window.addEventListener('error', (event) => {
      this.errors.push({
        message: event.message,
        file: event.filename,
        line: event.lineno,
        column: event.colno,
        time: new Date().toISOString()
      });
    });
  }

  getErrors() {
    return this.errors;
  }
}

6:数据上报

src/report.js 中:

1
2
3
4
5
6
7
8
9
10
export class Reporter {
  constructor(url) {
    this.url = url;
  }

  send(data) {
    const payload = JSON.stringify(data);
    navigator.sendBeacon(this.url, payload);
  }
}

7:整合 SDK

src/index.js 中:

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
import { PerformanceMonitor } from './performance.js';
import { ResourceMonitor } from './resource.js';
import { ErrorMonitor } from './error.js';
import { Reporter } from './report.js';

export class PerformanceSDK {
  constructor({ reportUrl }) {
    this.performance = new PerformanceMonitor();
    this.resource = new ResourceMonitor();
    this.error = new ErrorMonitor();
    this.reporter = new Reporter(reportUrl);
    this.reportInterval = null;
  }

  start() {
    this.reportInterval = setInterval(() => {
      const data = {
        performance: this.performance.getMetrics(),
        resources: this.resource.getResources(),
        errors: this.error.getErrors()
      };
      this.reporter.send(data);
    }, 5000);
  }

  stop() {
    clearInterval(this.reportInterval);
  }
}

8:打包 SDK

配置 rollup.config.js

1
2
3
4
5
6
7
8
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/performance-sdk.js',
    format: 'umd',
    name: 'PerformanceSDK'
  }
};

运行:

1
npx rollup -c

9:使用 SDK

1
2
3
4
5
<script src="/dist/performance-sdk.js"></script>
<script>
  const sdk = new PerformanceSDK({ reportUrl: 'https://example.com/report' });
  sdk.start();
</script>
This post is licensed under CC BY 4.0 by the author.