369 lines
9.7 KiB
JavaScript
369 lines
9.7 KiB
JavaScript
/**
|
|
* 性能监控模块
|
|
* 用于追踪和记录页面性能指标
|
|
*/
|
|
|
|
import { MONITOR_CONFIG } from './performance-config'
|
|
|
|
class PerformanceMonitor {
|
|
constructor() {
|
|
this.metrics = {
|
|
firstScreenLoadTime: 0,
|
|
imageLoadStats: {
|
|
total: 0,
|
|
success: 0,
|
|
failed: 0,
|
|
totalTime: 0
|
|
},
|
|
pollingStats: {
|
|
totalRequests: 0,
|
|
successRequests: 0,
|
|
failedRequests: 0,
|
|
totalResponseTime: 0
|
|
},
|
|
animationStats: {
|
|
fps: 0,
|
|
frameCount: 0
|
|
}
|
|
}
|
|
|
|
this.startTime = 0
|
|
this.fpsTimer = null
|
|
this.lastFrameTime = 0
|
|
this.isRunning = false
|
|
}
|
|
|
|
/**
|
|
* 检查监控是否启用
|
|
* @returns {boolean}
|
|
*/
|
|
isEnabled() {
|
|
// 如果设置了强制开关,使用强制开关的值
|
|
if (MONITOR_CONFIG.FORCE_ENABLE !== null) {
|
|
return MONITOR_CONFIG.FORCE_ENABLE
|
|
}
|
|
// 否则使用默认的 ENABLED 配置
|
|
return MONITOR_CONFIG.ENABLED
|
|
}
|
|
|
|
/**
|
|
* 开始监控
|
|
*/
|
|
start() {
|
|
if (!this.isEnabled()) {
|
|
console.log('[性能监控] 监控已禁用')
|
|
return
|
|
}
|
|
|
|
if (this.isRunning) {
|
|
console.warn('[性能监控] 监控已在运行中')
|
|
return
|
|
}
|
|
|
|
this.isRunning = true
|
|
this.startTime = Date.now()
|
|
console.log('[性能监控] 开始监控')
|
|
|
|
if (MONITOR_CONFIG.TRACK_FPS) {
|
|
this.startFPSTracking()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 停止监控
|
|
*/
|
|
stop() {
|
|
if (!this.isRunning) {
|
|
return
|
|
}
|
|
|
|
if (this.fpsTimer) {
|
|
// uni-app 兼容的取消动画帧
|
|
// #ifdef H5
|
|
cancelAnimationFrame(this.fpsTimer)
|
|
// #endif
|
|
// #ifndef H5
|
|
clearTimeout(this.fpsTimer)
|
|
// #endif
|
|
this.fpsTimer = null
|
|
}
|
|
|
|
this.isRunning = false
|
|
console.log('[性能监控] 停止监控')
|
|
|
|
// 如果配置了自动打印报告,则打印
|
|
if (MONITOR_CONFIG.AUTO_PRINT_REPORT && this.isEnabled()) {
|
|
this.printReport()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 记录首屏加载时间
|
|
*/
|
|
recordFirstScreenLoad() {
|
|
if (!this.isEnabled() || !this.isRunning) return
|
|
|
|
this.metrics.firstScreenLoadTime = Date.now() - this.startTime
|
|
this.log('info', `首屏加载时间: ${this.metrics.firstScreenLoadTime}ms`)
|
|
|
|
// 如果加载时间过长,发出警告
|
|
if (this.metrics.firstScreenLoadTime > 3000) {
|
|
this.log('warn', `首屏加载时间过长: ${this.metrics.firstScreenLoadTime}ms`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 记录图片加载
|
|
* @param {boolean} success - 是否成功
|
|
* @param {number} loadTime - 加载时间(毫秒)
|
|
*/
|
|
recordImageLoad(success, loadTime = 0) {
|
|
if (!this.isEnabled() || !this.isRunning) return
|
|
|
|
this.metrics.imageLoadStats.total++
|
|
if (success) {
|
|
this.metrics.imageLoadStats.success++
|
|
this.metrics.imageLoadStats.totalTime += loadTime
|
|
} else {
|
|
this.metrics.imageLoadStats.failed++
|
|
}
|
|
|
|
// 计算成功率
|
|
const successRate = (this.metrics.imageLoadStats.success / this.metrics.imageLoadStats.total * 100).toFixed(2)
|
|
const avgLoadTime = this.metrics.imageLoadStats.success > 0
|
|
? (this.metrics.imageLoadStats.totalTime / this.metrics.imageLoadStats.success).toFixed(0)
|
|
: 0
|
|
|
|
this.log('debug', `图片加载统计 - 成功率: ${successRate}%, 平均加载时间: ${avgLoadTime}ms`)
|
|
|
|
// 如果失败率过高,发出警告
|
|
if (this.metrics.imageLoadStats.failed / this.metrics.imageLoadStats.total > 0.3) {
|
|
this.log('warn', `图片加载失败率过高: ${(this.metrics.imageLoadStats.failed / this.metrics.imageLoadStats.total * 100).toFixed(2)}%`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 记录轮询请求
|
|
* @param {boolean} success - 是否成功
|
|
* @param {number} responseTime - 响应时间(毫秒)
|
|
*/
|
|
recordPollingRequest(success, responseTime = 0) {
|
|
if (!this.isEnabled() || !this.isRunning) return
|
|
|
|
this.metrics.pollingStats.totalRequests++
|
|
if (success) {
|
|
this.metrics.pollingStats.successRequests++
|
|
this.metrics.pollingStats.totalResponseTime += responseTime
|
|
} else {
|
|
this.metrics.pollingStats.failedRequests++
|
|
}
|
|
|
|
// 计算平均响应时间
|
|
const avgResponseTime = this.metrics.pollingStats.successRequests > 0
|
|
? (this.metrics.pollingStats.totalResponseTime / this.metrics.pollingStats.successRequests).toFixed(0)
|
|
: 0
|
|
|
|
this.log('debug', `轮询统计 - 平均响应时间: ${avgResponseTime}ms`)
|
|
|
|
// 如果响应时间过长,发出警告
|
|
if (responseTime > 5000) {
|
|
this.log('warn', `轮询响应时间过长: ${responseTime}ms`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始追踪 FPS
|
|
*/
|
|
startFPSTracking() {
|
|
if (!this.isEnabled()) return
|
|
|
|
let frameCount = 0
|
|
let lastTime = Date.now()
|
|
|
|
const trackFrame = () => {
|
|
frameCount++
|
|
const currentTime = Date.now()
|
|
|
|
// 每秒计算一次 FPS
|
|
if (currentTime - lastTime >= 1000) {
|
|
this.metrics.animationStats.fps = frameCount
|
|
this.metrics.animationStats.frameCount += frameCount
|
|
|
|
this.log('debug', `当前 FPS: ${frameCount}`)
|
|
|
|
// 如果 FPS 过低,发出警告
|
|
if (frameCount < 30) {
|
|
this.log('warn', `FPS 过低: ${frameCount}`)
|
|
}
|
|
|
|
frameCount = 0
|
|
lastTime = currentTime
|
|
}
|
|
|
|
if (this.isRunning) {
|
|
// uni-app 兼容的动画帧请求
|
|
// #ifdef H5
|
|
this.fpsTimer = requestAnimationFrame(trackFrame)
|
|
// #endif
|
|
// #ifndef H5
|
|
this.fpsTimer = setTimeout(trackFrame, 16) // 约 60fps
|
|
// #endif
|
|
}
|
|
}
|
|
|
|
trackFrame()
|
|
}
|
|
|
|
/**
|
|
* 获取性能报告
|
|
* @returns {Object} 性能指标对象
|
|
*/
|
|
getReport() {
|
|
const report = {
|
|
...this.metrics,
|
|
imageLoadSuccessRate: this.metrics.imageLoadStats.total > 0
|
|
? (this.metrics.imageLoadStats.success / this.metrics.imageLoadStats.total * 100).toFixed(2) + '%'
|
|
: '0%',
|
|
avgImageLoadTime: this.metrics.imageLoadStats.success > 0
|
|
? (this.metrics.imageLoadStats.totalTime / this.metrics.imageLoadStats.success).toFixed(0) + 'ms'
|
|
: '0ms',
|
|
pollingSuccessRate: this.metrics.pollingStats.totalRequests > 0
|
|
? (this.metrics.pollingStats.successRequests / this.metrics.pollingStats.totalRequests * 100).toFixed(2) + '%'
|
|
: '0%',
|
|
avgPollingResponseTime: this.metrics.pollingStats.successRequests > 0
|
|
? (this.metrics.pollingStats.totalResponseTime / this.metrics.pollingStats.successRequests).toFixed(0) + 'ms'
|
|
: '0ms'
|
|
}
|
|
|
|
return report
|
|
}
|
|
|
|
/**
|
|
* 打印性能报告
|
|
*/
|
|
printReport() {
|
|
if (!this.isEnabled()) return
|
|
|
|
const report = this.getReport()
|
|
console.log('========== 性能监控报告 ==========')
|
|
console.log(`首屏加载时间: ${report.firstScreenLoadTime}ms`)
|
|
console.log(`图片加载统计:`)
|
|
console.log(` - 总数: ${report.imageLoadStats.total}`)
|
|
console.log(` - 成功: ${report.imageLoadStats.success}`)
|
|
console.log(` - 失败: ${report.imageLoadStats.failed}`)
|
|
console.log(` - 成功率: ${report.imageLoadSuccessRate}`)
|
|
console.log(` - 平均加载时间: ${report.avgImageLoadTime}`)
|
|
console.log(`轮询统计:`)
|
|
console.log(` - 总请求数: ${report.pollingStats.totalRequests}`)
|
|
console.log(` - 成功: ${report.pollingStats.successRequests}`)
|
|
console.log(` - 失败: ${report.pollingStats.failedRequests}`)
|
|
console.log(` - 成功率: ${report.pollingSuccessRate}`)
|
|
console.log(` - 平均响应时间: ${report.avgPollingResponseTime}`)
|
|
if (MONITOR_CONFIG.TRACK_FPS) {
|
|
console.log(`动画统计:`)
|
|
console.log(` - 当前 FPS: ${report.animationStats.fps}`)
|
|
console.log(` - 总帧数: ${report.animationStats.frameCount}`)
|
|
}
|
|
console.log('==================================')
|
|
}
|
|
|
|
/**
|
|
* 重置统计数据
|
|
*/
|
|
reset() {
|
|
this.metrics = {
|
|
firstScreenLoadTime: 0,
|
|
imageLoadStats: {
|
|
total: 0,
|
|
success: 0,
|
|
failed: 0,
|
|
totalTime: 0
|
|
},
|
|
pollingStats: {
|
|
totalRequests: 0,
|
|
successRequests: 0,
|
|
failedRequests: 0,
|
|
totalResponseTime: 0
|
|
},
|
|
animationStats: {
|
|
fps: 0,
|
|
frameCount: 0
|
|
}
|
|
}
|
|
this.startTime = Date.now()
|
|
console.log('[性能监控] 统计数据已重置')
|
|
}
|
|
|
|
/**
|
|
* 启用监控(运行时控制)
|
|
*/
|
|
enable() {
|
|
MONITOR_CONFIG.FORCE_ENABLE = true
|
|
console.log('[性能监控] 已手动启用')
|
|
if (!this.isRunning) {
|
|
this.start()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 禁用监控(运行时控制)
|
|
*/
|
|
disable() {
|
|
MONITOR_CONFIG.FORCE_ENABLE = false
|
|
console.log('[性能监控] 已手动禁用')
|
|
if (this.isRunning) {
|
|
this.stop()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 日志输出
|
|
* @param {string} level - 日志级别
|
|
* @param {string} message - 日志消息
|
|
*/
|
|
log(level, message) {
|
|
if (!this.isEnabled()) return
|
|
|
|
const levels = ['debug', 'info', 'warn', 'error']
|
|
const configLevel = MONITOR_CONFIG.LOG_LEVEL
|
|
|
|
if (levels.indexOf(level) >= levels.indexOf(configLevel)) {
|
|
const prefix = '[性能监控]'
|
|
switch (level) {
|
|
case 'debug':
|
|
console.log(prefix, message)
|
|
break
|
|
case 'info':
|
|
console.info(prefix, message)
|
|
break
|
|
case 'warn':
|
|
console.warn(prefix, message)
|
|
break
|
|
case 'error':
|
|
console.error(prefix, message)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 创建单例实例
|
|
const performanceMonitor = new PerformanceMonitor()
|
|
|
|
export default performanceMonitor
|
|
|
|
// 导出常用方法
|
|
export const {
|
|
start,
|
|
stop,
|
|
recordFirstScreenLoad,
|
|
recordImageLoad,
|
|
recordPollingRequest,
|
|
getReport,
|
|
printReport,
|
|
reset,
|
|
enable,
|
|
disable,
|
|
isEnabled
|
|
} = performanceMonitor
|