如何设计一个 UniApp 项目的性能监控系统,能够同时监控 `App`、`H5` 和小程序端的性能指标?

各位观众老爷,大家好!今天咱们聊聊UniApp项目的性能监控,保证你的代码跑得飞起,用户体验蹭蹭上涨!

咱们的目标是:打造一个能同时监控App、H5和小程序三端的性能监控系统。听起来有点唬人,但别怕,一步一步来,你会发现其实也没那么难。

一、战略规划:监控啥?怎么监控?

在开始动手之前,我们需要明确两个问题:

  1. 监控哪些指标?

    • 加载时间: 首屏加载时间、资源加载时间(JS、CSS、图片等)。
    • 页面渲染时间: 页面DOM渲染时间。
    • 接口请求时间: API请求耗时、成功率。
    • JS错误: JS错误、Promise rejection。
    • 资源错误: 图片、CSS等资源加载失败。
    • 内存占用: App端内存占用情况。
    • CPU占用: App端CPU占用情况。
    • 用户行为: 页面PV、UV、点击事件等。
    • 白屏时间: 用户看到第一个像素点的时间。
    • 首次可交互时间 (TTI): 用户可以开始交互的时间。

    这些指标就像是你身体的各项体检指标,哪个不正常了,你就得赶紧看看是不是哪里出了问题。

  2. 怎么监控?

    • 埋点: 在关键代码处插入监控代码,记录性能数据。
    • API Hook: 拦截关键API,例如uni.request,记录请求信息。
    • 错误捕获: 监听全局错误事件,例如window.onerrorunhandledrejection
    • Performance API: 利用浏览器提供的Performance API,获取更详细的性能数据。

二、战术实施:代码实现

咱们分模块来,先打好地基,再盖高楼。

  1. 基础模块:数据收集

    首先,我们需要一个统一的数据收集模块,负责收集各种性能数据,并将其发送到服务器。

    // utils/monitor.js
    const API_URL = '你的监控服务器地址'; // 替换成你的服务器地址
    
    function sendData(data) {
      uni.request({
        url: API_URL,
        method: 'POST',
        data: data,
        header: {
          'Content-Type': 'application/json'
        }
      });
    }
    
    export default {
      log(type, data) {
        const reportData = {
          type: type,
          data: data,
          timestamp: Date.now(),
          platform: uni.getSystemInfoSync().platform, // 平台信息,方便区分App、H5和小程序
          appId: uni.getSystemInfoSync().appId || 'H5', //如果是H5则返回H5
        };
    
        sendData(reportData);
      },
    
      // 记录错误信息
      error(error) {
        this.log('error', {
          message: error.message,
          stack: error.stack,
        });
      },
    
      // 记录性能数据
      performance(name, value) {
        this.log('performance', {
          name: name,
          value: value,
        });
      },
    };

    这个monitor.js模块提供了三个主要方法:

    • log(type, data):用于记录各种类型的数据,例如错误、性能数据等。
    • error(error):专门用于记录错误信息。
    • performance(name, value):用于记录性能数据。

    这样,我们就有了一个统一的数据收集入口,所有需要上报的数据都通过这个模块发送到服务器。

  2. App端监控

    App端可以利用uni.getSystemInfoSync获取更详细的设备信息,还可以利用uni.onAppEnterForegrounduni.onAppEnterBackground监听App的前后台切换,记录App的活跃情况。

    // App.vue
    import monitor from './utils/monitor';
    
    export default {
      onLaunch: function() {
        console.log('App Launch');
    
        // 监听全局错误
        uni.onError(function(error) {
          monitor.error(error);
        });
    
        // 监听Promise rejection
        uni.onUnhandledRejection(function(error) {
          monitor.error(error);
        });
      },
      onShow: function() {
        console.log('App Show');
      },
      onHide: function() {
        console.log('App Hide');
      },
    };
  3. H5端监控

    H5端可以利用window.onerrorunhandledrejection监听全局错误,利用Performance API获取更详细的性能数据。

    // 在main.js或者其他入口文件中
    import monitor from './utils/monitor';
    
    // 监听全局错误
    window.onerror = function(message, source, lineno, colno, error) {
      monitor.error(error);
    };
    
    // 监听Promise rejection
    window.addEventListener('unhandledrejection', function(event) {
      monitor.error(event.reason);
    });
    
    // 监听白屏时间
    function getFirstPaintTime() {
      if (window.performance) {
        const timing = window.performance.timing;
        const firstPaintTime = timing.domContentLoadedEventStart - timing.navigationStart;
        monitor.performance('firstPaintTime', firstPaintTime);
      }
    }
    //在页面加载完成后获取
    window.onload = getFirstPaintTime;
    
    //监听首次可交互时间(TTI)
    function getTTI() {
      if ('PerformanceObserver' in window) {
        const observer = new PerformanceObserver((list) => {
          list.getEntries().forEach((entry) => {
            if (entry.name === 'dom-interactive') {
              monitor.performance('tti', entry.startTime);
              observer.disconnect();
            }
          });
        });
        observer.observe({ type: 'navigation', buffered: true });
      }
    }
    
    document.addEventListener('DOMContentLoaded', getTTI);
    

    H5端还可以通过addEventListener监听load事件,记录页面加载完成的时间。

  4. 小程序端监控

    小程序端与App端类似,可以使用uni.onErroruni.onUnhandledRejection监听全局错误,使用uni.getSystemInfoSync获取设备信息。

    // app.js
    import monitor from './utils/monitor';
    
    App({
      onLaunch: function() {
        // 监听全局错误
        uni.onError(function(error) {
          monitor.error(error);
        });
    
        // 监听Promise rejection
        uni.onUnhandledRejection(function(error) {
          monitor.error(error);
        });
      },
    });
  5. 接口请求监控

    我们可以通过uni.requestsuccessfail回调函数,记录接口请求的耗时和状态。

    // utils/request.js
    import monitor from './utils/monitor';
    
    const originalRequest = uni.request;
    
    uni.request = function(options) {
      const startTime = Date.now();
    
      return originalRequest({
        ...options,
        success: function(res) {
          const endTime = Date.now();
          const duration = endTime - startTime;
    
          monitor.performance('api_request', {
            url: options.url,
            duration: duration,
            statusCode: res.statusCode,
          });
    
          if (options.success) {
            options.success(res);
          }
        },
        fail: function(err) {
          const endTime = Date.now();
          const duration = endTime - startTime;
    
          monitor.error({
            message: `API request failed: ${options.url}`,
            error: err,
            duration: duration,
          });
    
          if (options.fail) {
            options.fail(err);
          }
        },
      });
    };
    
    export default uni.request;

    这段代码通过Hookuni.request方法,在每次请求前后记录时间戳,计算请求耗时,并将数据发送到服务器。

  6. 用户行为监控

    用户行为监控可以记录用户的点击事件、页面跳转等行为,帮助我们了解用户的使用习惯。

    // 在需要监控的页面中
    import monitor from '../../utils/monitor';
    
    export default {
      methods: {
        onClickButton() {
          // 记录点击事件
          monitor.log('click', {
            element: 'button',
            text: '点击按钮',
          });
    
          // 其他逻辑
        },
        onPageShow(){
            // 记录页面进入事件
            monitor.log('pageShow', {
              pageName: '当前页面名称',
            });
        }
      },
      onShow(){
        this.onPageShow()
      }
    };

三、服务器端:数据存储与分析

光收集数据还不够,我们需要一个服务器来接收、存储和分析这些数据。

  1. 数据接收

    服务器需要提供一个API接口,用于接收客户端发送的性能数据。

    // Node.js (Express) 示例
    const express = require('express');
    const bodyParser = require('body-parser');
    const app = express();
    const port = 3000;
    
    app.use(bodyParser.json());
    
    app.post('/api/monitor', (req, res) => {
      const data = req.body;
      console.log('Received monitor data:', data);
    
      // TODO: 将数据存储到数据库
    
      res.sendStatus(200);
    });
    
    app.listen(port, () => {
      console.log(`Server listening at http://localhost:${port}`);
    });

    这个简单的Node.js服务器接收客户端发送的JSON数据,并将其打印到控制台。你需要根据自己的实际情况,将数据存储到数据库中。

  2. 数据存储

    常用的数据库包括:

    • MySQL: 关系型数据库,适合存储结构化数据。
    • MongoDB: NoSQL数据库,适合存储半结构化数据,例如JSON格式的性能数据。
    • Elasticsearch: 搜索引擎,适合存储和分析大量的日志数据。

    选择哪种数据库取决于你的数据量、数据结构和分析需求。

  3. 数据分析

    有了数据,就可以进行各种分析了,例如:

    • 统计报表: 统计各种性能指标的平均值、最大值、最小值等。
    • 趋势分析: 分析性能指标随时间的变化趋势。
    • 异常告警: 当性能指标超过阈值时,发送告警通知。
    • 用户行为分析: 分析用户的使用习惯,优化产品设计。

    可以使用各种数据分析工具,例如:

    • Grafana: 数据可视化工具,可以将数据以图表的形式展示出来。
    • Kibana: Elasticsearch的可视化工具,可以用于分析日志数据。
    • 自定义脚本: 使用Python、R等脚本语言,编写自定义的分析逻辑。

四、优化建议

  • 数据采样: 如果数据量太大,可以采用数据采样的方式,只收集部分数据。
  • 数据压缩: 压缩数据可以减少网络传输的开销。
  • 异步发送: 异步发送数据可以避免阻塞主线程。
  • 错误上报: 错误上报需要包含足够的信息,例如错误信息、堆栈信息、设备信息等。
  • 性能监控: 性能监控本身也会消耗一定的性能,需要权衡监控的粒度和性能开销。

五、总结

咱们今天讲了UniApp性能监控系统的设计思路和实现方法,包括数据收集、App端监控、H5端监控、小程序端监控、接口请求监控、用户行为监控、服务器端数据存储与分析等方面。

记住,性能监控是一个持续的过程,需要不断地优化和改进。希望今天的分享对你有所帮助,祝你的UniApp项目性能越来越好!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注