React 性能监控与错误上报
核心问题与技术挑战
现代 React 应用随着业务复杂度增加,性能问题和运行时错误日益成为影响用户体验的关键因素。没有可靠的监控与错误上报机制,我们将陷入被动修复而非主动预防的困境。
性能指标体系与错误分类
关键性能指标定义
// performance-metrics.js
export const PERFORMANCE_METRICS = {// 页面加载指标FP: 'first-paint', // 首次绘制FCP: 'first-contentful-paint', // 首次内容绘制LCP: 'largest-contentful-paint', // 最大内容绘制FID: 'first-input-delay', // 首次输入延迟TTI: 'time-to-interactive', // 可交互时间TBT: 'total-blocking-time', // 总阻塞时间CLS: 'cumulative-layout-shift', // 累积布局偏移// React 特有指标COMPONENT_RENDER_TIME: 'component-render-time', // 组件渲染时间EFFECT_TIME: 'effect-execution-time', // Effect执行时间RERENDER_COUNT: 'component-rerender-count', // 组件重渲染次数CONTEXT_CHANGES: 'context-change-frequency', // Context变更频率MEMO_HIT_RATE: 'memo-hit-rate', // memo命中率
};
错误分类与等级定义
// error-classification.js
export const ERROR_TYPES = {RENDER_ERROR: 'render-error', // 渲染错误EFFECT_ERROR: 'effect-error', // 副作用错误EVENT_HANDLER_ERROR: 'event-error', // 事件处理错误ASYNC_ERROR: 'async-error', // 异步操作错误RESOURCE_LOADING_ERROR: 'resource-error', // 资源加载错误API_ERROR: 'api-error', // API请求错误UNCAUGHT_ERROR: 'uncaught-error', // 未捕获的错误
};export const ERROR_LEVELS = {FATAL: 'fatal', // 致命错误:导致应用崩溃或核心功能无法使用ERROR: 'error', // 错误:功能无法正常工作,但不影响整体应用WARNING: 'warning', // 警告:可能存在问题,但功能仍可使用INFO: 'info', // 信息:值得注意的异常状况但无功能影响
};
监控工具选型与评估
Ran tool
监控工具对比分析
工具名称 | 类型 | 性能监控能力 | 错误捕获 | 集成复杂度 | 数据所有权 | 成本结构 | 适用场景 |
---|---|---|---|---|---|---|---|
React Profiler | 内置 | 中(组件级) | 无 | 低 | 完全自有 | 免费 | 开发调试 |
Performance API | 内置 | 高 | 无 | 中 | 完全自有 | 免费 | 核心指标采集 |
Sentry | 第三方 | 中 | 强 | 低 | 外部存储 | 免费起步,按量付费 | 中小型应用 |
LogRocket | 第三方 | 高 | 强 | 低 | 外部存储 | 付费 | 用户体验分析 |
自建系统 | 自研 | 可定制 | 可定制 | 高 | 完全自有 | 开发+维护成本 | 大型复杂应用 |
自定义监控系统架构设计
// 项目结构
/src/monitoring/performancemetrics-collector.jsrender-tracker.jsinteraction-tracker.js/errorserror-boundary.jserror-handler.jsapi-error-tracker.js/reportingdata-aggregator.jstransport-layer.jsbatch-processor.js/configsampling-rules.jsmetrics-thresholds.jsindex.js
性能监控核心实现
性能数据采集器
// metrics-collector.js
import { PERFORMANCE_METRICS } from '../config/metrics-definitions';class PerformanceMetricsCollector {metrics = new Map();markTimestamps = new Map();mark(name) {this.markTimestamps.set(name, performance.now());// 兼容 performance.mark APIif (performance.mark) {performance.mark(`${name}-start`);}}measure(name, startMark) {if (!this.markTimestamps.has(startMark)) {console.warn(`Start mark "${startMark}" not found for measure "${name}"`);return;}const startTime = this.markTimestamps.get(startMark);const duration = performance.now() - startTime;// 记录此次测量值if (!this.metrics.has(name)) {this.metrics.set(name, []);}this.metrics.get(name).push(duration);// 兼容 performance.measure APIif (performance.measure) {try {performance.measure(name, `${startMark}-start`);} catch (e) {// 某些浏览器在mark不存在时会抛出异常}}return duration;}getMetrics(name) {if (!this.metrics.has(name)) return null;const values = this.metrics.get(name);return {name,values,min: Math.min(...values),max: Math.max(...values),avg: values.reduce((sum, val) => sum + val, 0) / values.length,median: this.calculateMedian(values),p95: this.calculatePercentile(values, 95),};}getAllMetrics() {const result = {};this.metrics.forEach((values, name) => {result[name] = this.getMetrics(name);});return result;}calculateMedian(values) {if (!values.length) return 0;const sorted = [...values].sort((a, b) => a - b);const middle = Math.floor(sorted.length / 2);return sorted.length % 2 === 0? (sorted[middle - 1] + sorted[middle]) / 2: sorted[middle];}calculatePercentile(values, percentile) {if (!values.length) return 0;const sorted = [...values].sort((a, b) => a - b);const index = Math.ceil((percentile / 100) * sorted.length) - 1;return sorted[index];}// Web Vitals指标采集captureWebVitals() {const { onLCP, onFID, onCLS, onTTFB } = require('web-vitals');onLCP(({ value }) => {if (!this.metrics.has(PERFORMANCE_METRICS.LCP)) {this.metrics.set(PERFORMANCE_METRICS.LCP, []);}this.metrics.get(PERFORMANCE_METRICS.LCP).push(value);this.reportMetric(PERFORMANCE_METRICS.LCP, value);});onFID(({ value }) => {if (!this.metrics.has(PERFORMANCE_METRICS.FID)) {this.metrics.set(PERFORMANCE_METRICS.FID, []);}this.metrics.get(PERFORMANCE_METRICS.FID).push(value);this.reportMetric(PERFORMANCE_METRICS.FID, value);});onCLS(({ value }) => {if (!this.metrics.has(PERFORMANCE_METRICS.CLS)) {this.metrics.set(PERFORMANCE_METRICS.CLS, []);}this.metrics.get(PERFORMANCE_METRICS.CLS).push(value);this.reportMetric(PERFORMANCE_METRICS.CLS, value);});onTTFB(({ value }) => {if (!this.metrics.has('TTFB')) {this.metrics.set('TTFB', []);}this.metrics.get('TTFB').push(value);this.reportMetric('TTFB', value);});}reportMetric(name, value) {// 将指标发送到上报系统if (this.reporter) {this.reporter.sendMetric({name,value,timestamp: Date.now()});}}setReporter(reporter) {this.reporter = reporter;}
}export const metricsCollector = new PerformanceMetricsCollector();
export default metricsCollector;
React 组件性能追踪 HOC
// render-tracker.js
import React, { Component } from 'react';
import metricsCollector from './metrics-collector';
import { PERFORMANCE_METRICS } from '../config/metrics-definitions';// 追踪组件渲染性能的高阶组件
export function withRenderTracking(WrappedComponent, options = {}) {const {trackProps = false,trackState = false,componentName = WrappedComponent.displayName || WrappedComponent.name || 'Component',threshold = 16, // 默认阈值16ms (60fps)} = options;return class RenderTracker extends Component {static displayName = `RenderTracker(${componentName})`;// 记录重渲染次数rerenderCount = 0;// 用于记录渲染前props和stateprevProps = null;prevState = null;componentDidMount() {this.recordMounting();}shouldComponentUpdate(nextProps, nextState) {this.prevProps = this.props;this.prevState = this.state;return true;}componentDidUpdate() {this.rerenderCount++;this.recordRerender();if (trackProps && this.prevProps) {const changedProps = this.getChangedProps(this.prevProps, this.props);if (Object.keys(changedProps).length > 0) {this.recordPropChanges(changedProps);}}if (trackState && this.prevState) {const changedState = this.getChangedProps(this.prevState, this.state);if (Object.keys(changedState).length > 0) {this.recordStateChanges(changedState);}}}getChangedProps(prev, current) {const changes = {};Object.keys(current).forEach(key => {if (prev[key] !== current[key]) {changes[key] = {from: prev[key],to: current[key]};}});return changes;}recordMounting() {const renderTime = metricsCollector.measure(`${componentName}-mount`,`${componentName}-render-start`);if (renderTime > threshold) {console.warn(`[Performance] Component ${componentName} took ${renderTime.toFixed(2)}ms to mount, ` +`which exceeds the threshold of ${threshold}ms.`);}metricsCollector.reportMetric(PERFORMANCE_METRICS.COMPONENT_RENDER_TIME,{ componentName, phase: 'mount', duration: renderTime });}recordRerender() {const renderTime = metricsCollector.measure(`${componentName}-rerender-${this.rerenderCount}`,`${componentName}-render-start`);if (renderTime > threshold) {console.warn(`[Performance] Component ${componentName} took ${renderTime.toFixed(2)}ms to rerender, ` +`which exceeds the threshold of ${threshold}ms. (Rerender #${this.rerenderCount})`);}metricsCollector.reportMetric(PERFORMANCE_METRICS.COMPONENT_RENDER_TIME,{ componentName, phase: 'update', count: this.rerenderCount, duration: renderTime });metricsCollector.reportMetric(PERFORMANCE_METRICS.RERENDER_COUNT,{ componentName, count: this.rerenderCount });}recordPropChanges(changedProps) {metricsCollector.reportMetric('prop-changes',{ componentName, changes: changedProps });}recordStateChanges(changedState) {metricsCollector.reportMetric('state-changes',{ componentName, changes: changedState });}render() {metricsCollector.mark(`${componentName}-render-start`);return <WrappedComponent {...this.props} />;}};
}// 针对函数组件的性能追踪Hook
export function useRenderTracking(componentName, options = {}) {const {threshold = 16} = options;const renderCount = React.useRef(0);React.useEffect(() => {const renderTime = metricsCollector.measure(`${componentName}-render-${renderCount.current}`,`${componentName}-render-start`);if (renderTime > threshold) {console.warn(`[Performance] Component ${componentName} took ${renderTime.toFixed(2)}ms to render, ` +`which exceeds the threshold of ${threshold}ms. (Render #${renderCount.current})`);}metricsCollector.reportMetric(PERFORMANCE_METRICS.COMPONENT_RENDER_TIME,{ componentName, count: renderCount.current, duration: renderTime });renderCount.current++;});// 在组件渲染之前标记React.useLayoutEffect(() => {metricsCollector.mark(`${componentName}-render-start`);}, [componentName]);return renderCount.current;
}
错误监控与上报系统
全局错误边界组件
// error-boundary.js
import React, { Component } from 'react';
import { ERROR_TYPES, ERROR_LEVELS } from '../config/error-classification';
import errorReporter from './error-reporter';export class ErrorBoundary extends Component {static defaultProps = {fallback: null,onError: null,errorLevel: ERROR_LEVELS.ERROR,componentName: 'Unknown',};state = {hasError: false,error: null,errorInfo: null};componentDidCatch(error, errorInfo) {const { componentName, errorLevel, onError } = this.props;// 更新组件状态this.setState({hasError: true,error,errorInfo});// 获取组件树结构const componentStack = errorInfo?.componentStack || '';// 构造错误信息const errorData = {type: ERROR_TYPES.RENDER_ERROR,level: errorLevel,message: error.message,stack: error.stack,componentStack,componentName,time: Date.now(),url: window.location.href,userAgent: navigator.userAgent,// 错误的额外上下文context: {route: window.location.pathname,...this.props.errorContext}};// 上报错误errorReporter.reportError(errorData);// 调用父组件错误处理函数if (typeof onError === 'function') {onError(error, errorInfo);}// 记录到控制台console.error('[ErrorBoundary]', error, errorInfo);}resetError = () => {this.setState({hasError: false,error: null,errorInfo: null});};render() {const { fallback, children } = this.props;const { hasError, error, errorInfo } = this.state;if (hasError) {// 提供重置错误的方法给fallback组件const resetHandler = {resetError: this.resetError};// 如果提供了fallback组件if (fallback) {if (React.isValidElement(fallback)) {return React.cloneElement(fallback, {error,errorInfo,...resetHandler});} else if (typeof fallback === 'function') {return fallback({error,errorInfo,...resetHandler});}}// 默认错误UIreturn (<div className="error-boundary-fallback"><h2>组件渲染出错</h2><p>抱歉,组件渲染出现了问题。请尝试刷新页面或联系技术支持。</p><button onClick={this.resetError}>重试</button></div>);}return children;}
}// 高阶组件封装
export function withErrorBoundary(Component, options = {}) {const {fallback,onError,errorLevel = ERROR_LEVELS.ERROR,errorContext = {}} = options;const componentName = Component.displayName || Component.name || 'Unknown';const WrappedComponent = (props) => (<ErrorBoundaryfallback={fallback}onError={onError}errorLevel={errorLevel}componentName={componentName}errorContext={{...errorContext,props: Object.keys(props)}}><Component {...props} /></ErrorBoundary>);WrappedComponent.displayName = `withErrorBoundary(${componentName})`;return WrappedComponent;
}
全局错误捕获服务
// error-handler.js
import { ERROR_TYPES, ERROR_LEVELS } from '../config/error-classification';class ErrorHandler {constructor(reporter) {this.reporter = reporter;this.initialized = false;this.ignorePatterns = [// 忽略一些非关键或第三方错误/Script error\./i,/ResizeObserver loop limit exceeded/i,];}initialize() {if (this.initialized) return;// 捕获未处理的Promise异常window.addEventListener('unhandledrejection', this.handlePromiseRejection);// 捕获全局JavaScript错误window.addEventListener('error', this.handleWindowError, true);// 拦截console.error (可选)if (this.options?.interceptConsoleError) {this.originalConsoleError = console.error;console.error = (...args) => {this.handleConsoleError(...args);this.originalConsoleError.apply(console, args);};}this.initialized = true;console.log('[ErrorHandler] Global error handler initialized');}setOptions(options = {}) {this.options = {captureUnhandledRejections: true,captureGlobalErrors: true,interceptConsoleError: false,samplingRate: 1.0, // 1.0 = 捕获所有错误maxErrorsPerMinute: 10,...options};}setReporter(reporter) {this.reporter = reporter;}handleWindowError = (event) => {// 阻止浏览器默认错误处理event.preventDefault();const { message, filename, lineno, colno, error } = event;// 检查是否应忽略此错误if (this.shouldIgnoreError(message)) {return true;}// 构造错误信息const errorData = {type: ERROR_TYPES.UNCAUGHT_ERROR,level: ERROR_LEVELS.FATAL,message,stack: error?.stack || '',source: {file: filename,line: lineno,column: colno},time: Date.now(),url: window.location.href,userAgent: navigator.userAgent};// 上报错误this.reportError(errorData);return true;};handlePromiseRejection = (event) => {// 组装有意义的错误信息const error = event.reason;const message = error?.message || 'Unhandled Promise Rejection';// 检查是否应忽略此错误if (this.shouldIgnoreError(message)) {return;}const errorData = {type: ERROR_TYPES.ASYNC_ERROR,level: ERROR_LEVELS.ERROR,message,stack: error?.stack || '',time: Date.now(),url: window.location.href,userAgent: navigator.userAgent};// 上报错误this.reportError(errorData);};handleConsoleError = (...args) => {// 提取有意义的错误信息const errorMessage = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ');// 检查是否应忽略此错误if (this.shouldIgnoreError(errorMessage)) {return;}const errorData = {type: ERROR_TYPES.CONSOLE_ERROR,level: ERROR_LEVELS.WARNING,message: errorMessage.slice(0, 500), // 限制长度time: Date.now(),url: window.location.href};// 上报错误this.reportError(errorData);};shouldIgnoreError(message) {// 检查是否匹配忽略模式return this.ignorePatterns.some(pattern => pattern.test(message));}addIgnorePattern(pattern) {if (pattern instanceof RegExp) {this.ignorePatterns.push(pattern);} else if (typeof pattern === 'string') {this.ignorePatterns.push(new RegExp(pattern, 'i'));}}reportError(errorData) {// 采样控制if (Math.random() > this.options?.samplingRate) {return;}// 限流控制if (this.isRateLimited()) {return;}// 使用上报服务发送错误if (this.reporter) {this.reporter.sendError(errorData);}}// 实现错误上报频率限制isRateLimited() {const now = Date.now();const maxPerMinute = this.options?.maxErrorsPerMinute || 10;if (!this._errorTimestamps) {this._errorTimestamps = [];}// 清理一分钟前的错误记录this._errorTimestamps = this._errorTimestamps.filter(timestamp => now - timestamp < 60000);// 检查是否超出限制if (this._errorTimestamps.length >= maxPerMinute) {return true;}// 记录当前错误时间戳this._errorTimestamps.push(now);return false;}// 清理资源destroy() {if (!this.initialized) return;window.removeEventListener('unhandledrejection', this.handlePromiseRejection);window.removeEventListener('error', this.handleWindowError, true);if (this.originalConsoleError) {console.error = this.originalConsoleError;}this.initialized = false;}
}export const errorHandler = new ErrorHandler();
export default errorHandler;
API 错误跟踪器
// api-error-tracker.js
import { ERROR_TYPES, ERROR_LEVELS } from '../config/error-classification';// 创建拦截器以追踪API请求错误
export function createAPIErrorTracker(reporter) {// Fetch API拦截const originalFetch = window.fetch;window.fetch = async function trackedFetch(url, options = {}) {const startTime = performance.now();const requestId = generateRequestId();// 捕获请求信息const requestInfo = {url: typeof url === 'string' ? url : url.url,method: options.method || 'GET',requestId,startTime};try {// 记录请求开始reporter.sendMetric({name: 'api-request-start',value: requestInfo});// 执行原始请求const response = await originalFetch.apply(this, arguments);// 计算请求时间const duration = performance.now() - startTime;// 处理非2xx响应if (!response.ok) {let responseBody = '';try {// 克隆响应以便仍可读取主体const clonedResponse = response.clone();responseBody = await clonedResponse.text();} catch (e) {// 无法读取响应体responseBody = 'Unable to read response body';}// 上报API错误const errorData = {type: ERROR_TYPES.API_ERROR,level: response.status >= 500 ? ERROR_LEVELS.ERROR : ERROR_LEVELS.WARNING,message: `API Error: ${response.status} ${response.statusText}`,time: Date.now(),url: window.location.href,context: {request: {url: requestInfo.url,method: requestInfo.method,requestId},response: {status: response.status,statusText: response.statusText,body: responseBody.substring(0, 1000) // 限制大小},duration}};reporter.sendError(errorData);}// 记录请求完成reporter.sendMetric({name: 'api-request-complete',value: {...requestInfo,status: response.status,duration,success: response.ok}});return response;} catch (error) {// 计算请求时间const duration = performance.now() - startTime;// 上报网络错误const errorData = {type: ERROR_TYPES.API_ERROR,level: ERROR_LEVELS.ERROR,message: `Network Error: ${error.message}`,stack: error.stack,time: Date.now(),url: window.location.href,context: {request: {url: requestInfo.url,method: requestInfo.method,requestId},error: error.message,duration}};reporter.sendError(errorData);// 记录请求失败reporter.sendMetric({name: 'api-request-failed',value: {...requestInfo,error: error.message,duration,success: false}});// 重新抛出原始错误throw error;}};// Axios拦截器(如果项目使用Axios)if (window.axios) {window.axios.interceptors.request.use(config => {config.requestId = generateRequestId();config.startTime = performance.now();// 记录请求开始reporter.sendMetric({name: 'api-request-start',value: {url: config.url,method: config.method,requestId: config.requestId,startTime: config.startTime}});return config;});window.axios.interceptors.response.use(response => {const { config } = response;const duration = performance.now() - config.startTime;// 记录请求完成reporter.sendMetric({name: 'api-request-complete',value: {url: config.url,method: config.method,requestId: config.requestId,status: response.status,duration,success: true}});return response;},error => {const { config, response } = error;// 某些情况下请求可能未发出if (!config) {return Promise.reject(error);}const duration = performance.now() - config.startTime;// 上报API错误const errorData = {type: ERROR_TYPES.API_ERROR,level: response?.status >= 500 ? ERROR_LEVELS.ERROR : ERROR_LEVELS.WARNING,message: `API Error: ${response?.status || 'Network Error'} ${error.message}`,time: Date.now(),url: window.location.href,context: {request: {url: config.url,method: config.method,requestId: config.requestId},response: response ? {status: response.status,statusText: response.statusText,data: response.data} : null,duration}};reporter.sendError(errorData);// 记录请求失败reporter.sendMetric({name: 'api-request-failed',value: {url: config.url,method: config.method,requestId: config.requestId,error: error.message,status: response?.status,duration,success: false}});return Promise.reject(error);});}// 生成请求IDfunction generateRequestId() {return `req_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;}return {// 恢复原始方法restore: () => {window.fetch = originalFetch;if (window.axios) {window.axios.interceptors.request.eject(0);window.axios.interceptors.response.eject(0);}}};
}
数据上报与聚合系统
上报传输层
// transport-layer.js
class DataTransport {constructor(options = {}) {this.options = {endpoint: '/monitoring/collect',batchSize: 10,flushInterval: 5000, // 5秒retryAttempts: 3,retryDelay: 1000,useBeacon: true,...options};this.buffer = [];this.isSending = false;this.flushTimer = null;this.retryQueue = [];// 启动定期刷新this.startPeriodicFlush();// 页面卸载时发送所有待处理数据window.addEventListener('beforeunload', this.flushBeforeUnload);}setEndpoint(endpoint) {this.options.endpoint = endpoint;}send(data) {// 添加通用字段const enrichedData = {...data,timestamp: data.timestamp || Date.now(),session: this.getSessionInfo(),user: this.getUserInfo(),app: this.getAppInfo()};// 添加到缓冲区this.buffer.push(enrichedData);// 如果达到批处理大小,立即发送if (this.buffer.length >= this.options.batchSize) {this.flush();}return true;}async flush() {if (this.isSending || this.buffer.length === 0) {return;}this.isSending = true;// 提取当前缓冲区数据const dataToSend = [...this.buffer];this.buffer = [];try {// 尝试发送数据const success = await this.sendData(dataToSend);if (!success) {// 如果发送失败,将数据添加到重试队列this.addToRetryQueue(dataToSend);}} catch (error) {console.error('[Monitoring] Error sending monitoring data:', error);// 发送失败,添加到重试队列this.addToRetryQueue(dataToSend);}this.isSending = false;// 处理重试队列if (this.retryQueue.length > 0) {this.processRetryQueue();}}async sendData(data) {// 检查页面是否正在卸载if (this.isUnloading && this.options.useBeacon && navigator.sendBeacon) {// 使用Beacon API发送数据(更可靠地处理页面卸载场景)const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });return navigator.sendBeacon(this.options.endpoint, blob);} else {// 使用标准fetch APItry {const response = await fetch(this.options.endpoint, {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify(data),// 不跟随重定向redirect: 'error',// 发送凭据(例如cookies)credentials: 'same-origin',// 设置较短的超时时间signal: AbortSignal.timeout(10000) // 10秒超时});return response.ok;} catch (error) {console.error('[Monitoring] Transport error:', error);return false;}}}addToRetryQueue(data) {// 添加到重试队列,并记录重试次数this.retryQueue.push({data,attempts: 0,nextRetry: Date.now() + this.options.retryDelay});}async processRetryQueue() {if (this.isProcessingRetryQueue) {return;}this.isProcessingRetryQueue = true;const now = Date.now();const itemsToRetry = this.retryQueue.filter(item => item.nextRetry <= now);// 更新重试队列,删除将要重试的项目this.retryQueue = this.retryQueue.filter(item => item.nextRetry > now);for (const item of itemsToRetry) {// 增加重试次数item.attempts++;try {const success = await this.sendData(item.data);if (!success && item.attempts < this.options.retryAttempts) {// 计算下一次重试时间(使用指数退避)const nextRetryDelay = this.options.retryDelay * Math.pow(2, item.attempts - 1);item.nextRetry = Date.now() + nextRetryDelay;// 重新添加到队列this.retryQueue.push(item);}} catch (error) {if (item.attempts < this.options.retryAttempts) {// 计算下一次重试时间const nextRetryDelay = this.options.retryDelay * Math.pow(2, item.attempts - 1);item.nextRetry = Date.now() + nextRetryDelay;// 重新添加到队列this.retryQueue.push(item);}}}this.isProcessingRetryQueue = false;}startPeriodicFlush() {// 定期刷新缓冲区this.flushTimer = setInterval(() => {if (this.buffer.length > 0) {this.flush();}// 处理重试队列if (this.retryQueue.length > 0) {this.processRetryQueue();}}, this.options.flushInterval);}flushBeforeUnload = () => {// 标记正在卸载,这会使sendData使用navigator.beaconthis.isUnloading = true;// 取消计时器clearInterval(this.flushTimer);// 合并重试队列和当前缓冲区const allPendingData = [...this.buffer,...this.retryQueue.map(item => item.data).flat()];if (allPendingData.length > 0) {// 使用同步方式发送所有数据const blob = new Blob([JSON.stringify(allPendingData)], { type: 'application/json' });navigator.sendBeacon(this.options.endpoint, blob);}};getSessionInfo() {// 在实际应用中,应该从会话管理系统获取这些信息return {id: window.sessionStorage.getItem('session_id') || 'unknown',startedAt: parseInt(window.sessionStorage.getItem('session_start') || Date.now()),pageViews: parseInt(window.sessionStorage.getItem('page_views') || '1')};}getUserInfo() {// 在实际应用中,应该从身份验证系统获取这些信息// 注意:确保遵守隐私法规和公司政策return {// 使用匿名ID或经过同意的用户标识符id: window.localStorage.getItem('user_id') || 'anonymous',// 可以添加经过许可的用户属性type: window.localStorage.getItem('user_type') || 'visitor'};}getAppInfo() {return {name: window.APP_NAME || document.title,version: window.APP_VERSION || '1.0',environment: window.APP_ENV || process.env.NODE_ENV || 'production',reactVersion: React.version,viewportSize: `${window.innerWidth}x${window.innerHeight}`,language: navigator.language,platform: navigator.platform};}destroy() {// 清理资源clearInterval(this.flushTimer);window.removeEventListener('beforeunload', this.flushBeforeUnload);// 发送所有待处理数据if (this.buffer.length > 0 || this.retryQueue.length > 0) {this.flushBeforeUnload();}}
}export const dataTransport = new DataTransport();
export default dataTransport;
数据聚合与批处理器
// batch-processor.js
import dataTransport from './transport-layer';class MonitoringReporter {constructor(transport) {this.transport = transport;this.metricsBuffer = {};this.errorBuffer = [];this.flushInterval = 10000; // 10秒this.bufferSize = {metrics: 20,errors: 5};// 启动定期批处理this.startPeriodicFlush();}// 发送性能指标sendMetric(metric) {const { name, value } = metric;// 按指标类型进行分组if (!this.metricsBuffer[name]) {this.metricsBuffer[name] = [];}// 添加时间戳const metricWithTimestamp = {...metric,timestamp: metric.timestamp || Date.now()};// 添加到缓冲区this.metricsBuffer[name].push(metricWithTimestamp);// 如果该类型的指标达到阈值,就发送此类型的所有指标if (this.metricsBuffer[name].length >= this.bufferSize.metrics) {this.flushMetricsByType(name);}return true;}// 发送错误sendError(error) {// 添加到错误缓冲区this.errorBuffer.push({...error,timestamp: error.timestamp || Date.now()});// 如果错误达到阈值,立即发送if (this.errorBuffer.length >= this.bufferSize.errors) {this.flushErrors();}return true;}// 按指标类型刷新缓冲区flushMetricsByType(metricType) {if (!this.metricsBuffer[metricType] || this.metricsBuffer[metricType].length === 0) {return;}// 提取要发送的指标const metricsToSend = [...this.metricsBuffer[metricType]];// 清空缓冲区this.metricsBuffer[metricType] = [];// 构造批量数据包const payload = {type: 'metric',metricType,data: metricsToSend};// 发送到传输层this.transport.send(payload);}// 刷新所有错误flushErrors() {if (this.errorBuffer.length === 0) {return;}// 提取要发送的错误const errorsToSend = [...this.errorBuffer];// 清空缓冲区this.errorBuffer = [];// 构造批量数据包const payload = {type: 'error',data: errorsToSend};// 发送到传输层this.transport.send(payload);}// 刷新所有指标flushAllMetrics() {// 遍历所有指标类型Object.keys(this.metricsBuffer).forEach(metricType => {if (this.metricsBuffer[metricType].length > 0) {this.flushMetricsByType(metricType);}});}// 刷新所有数据flushAll() {this.flushAllMetrics();this.flushErrors();}// 启动定期刷新startPeriodicFlush() {this.flushTimer = setInterval(() => {this.flushAll();}, this.flushInterval);// 页面隐藏时刷新数据document.addEventListener('visibilitychange', () => {if (document.visibilityState === 'hidden') {this.flushAll();}});}// 设置缓冲区大小setBufferSize(type, size) {if (type === 'metrics' || type === 'errors') {this.bufferSize[type] = size;}}// 销毁实例,清理资源destroy() {clearInterval(this.flushTimer);this.flushAll();}
}export const reporter = new MonitoringReporter(dataTransport);
export default reporter;
系统集成与自动化配置
监控系统初始化
// index.js
import React from 'react';
import { PERFORMANCE_METRICS } from './config/metrics-definitions';
import { ERROR_LEVELS } from './config/error-classification';
import metricsCollector from './performance/metrics-collector';
import { withRenderTracking, useRenderTracking } from './performance/render-tracker';
import { ErrorBoundary, withErrorBoundary } from './errors/error-boundary';
import errorHandler from './errors/error-handler';
import { createAPIErrorTracker } from './errors/api-error-tracker';
import reporter from './reporting/batch-processor';
import dataTransport from './reporting/transport-layer';// 默认配置
const defaultConfig = {enablePerformanceMonitoring: true,enableErrorMonitoring: true,errorReportingEndpoint: '/api/error-reporting',metricsReportingEndpoint: '/api/metrics-reporting',samplingRate: 0.1, // 采样 10% 的用户logLevel: ERROR_LEVELS.ERROR, // 仅报告错误及以上级别maxErrorsPerMinute: 10,captureConsoleErrors: false,enableReactProfiling: false,
};// 监控系统主类
class ReactMonitoring {constructor() {this.initialized = false;this.config = { ...defaultConfig };}init(userConfig = {}) {if (this.initialized) {console.warn('[ReactMonitoring] Already initialized. Call destroy() first if you need to reinitialize.');return this;}// 合并用户配置this.config = {...defaultConfig,...userConfig,};// 随机采样决定是否为这个用户启用监控const shouldMonitor = Math.random() < this.config.samplingRate;if (!shouldMonitor) {console.log('[ReactMonitoring] This session was not selected for monitoring (sampling)');return this;}// 配置数据传输层dataTransport.setEndpoint(this.config.errorReportingEndpoint);// 初始化错误处理if (this.config.enableErrorMonitoring) {this.initErrorMonitoring();}// 初始化性能监控if (this.config.enablePerformanceMonitoring) {this.initPerformanceMonitoring();}this.initialized = true;console.log('[ReactMonitoring] Initialized successfully');return this;}initErrorMonitoring() {// 配置错误处理器errorHandler.setOptions({captureUnhandledRejections: true,captureGlobalErrors: true,interceptConsoleError: this.config.captureConsoleErrors,samplingRate: 1.0, // 捕获所有发生的错误maxErrorsPerMinute: this.config.maxErrorsPerMinute,});// 设置错误上报服务errorHandler.setReporter(reporter);// 初始化全局错误处理errorHandler.initialize();// 创建API错误跟踪器this.apiErrorTracker = createAPIErrorTracker(reporter);console.log('[ReactMonitoring] Error monitoring initialized');}initPerformanceMonitoring() {// 配置指标收集器metricsCollector.setReporter(reporter);// 捕获Web VitalsmetricsCollector.captureWebVitals();// 捕获首次加载性能this.captureInitialPerformance();if (this.config.enableReactProfiling && window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {this.setupReactProfiling();}console.log('[ReactMonitoring] Performance monitoring initialized');}captureInitialPerformance() {// 利用Performance Timeline API捕获关键性能指标if (window.performance && performance.timing) {// 等待加载完成if (document.readyState === 'complete') {this.processPerformanceTiming();} else {window.addEventListener('load', () => {// 延迟一下以确保所有指标都已可用setTimeout(() => this.processPerformanceTiming(), 0);});}}}processPerformanceTiming() {const timing = performance.timing;// 计算关键性能指标const metrics = {// DNS解析时间dns: timing.domainLookupEnd - timing.domainLookupStart,// TCP连接时间tcp: timing.connectEnd - timing.connectStart,// 请求时间request: timing.responseStart - timing.requestStart,// 响应时间response: timing.responseEnd - timing.responseStart,// DOM解析时间domParse: timing.domInteractive - timing.responseEnd,// DOM内容加载domContentLoaded: timing.domContentLoadedEventEnd - timing.domContentLoadedEventStart,// DOM完全加载domComplete: timing.domComplete - timing.domLoading,// 页面完全加载时间pageLoad: timing.loadEventEnd - timing.navigationStart,// 首次渲染时间(近似)firstPaint: timing.domLoading - timing.navigationStart,};// 上报指标Object.entries(metrics).forEach(([name, value]) => {reporter.sendMetric({name: `page_${name}`,value,category: 'page-load',});});}setupReactProfiling() {// 这需要React DevTools扩展的钩子const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;if (hook && hook.supportsFiber) {// 获取React实例const renderers = hook.getFiberRoots ? hook.getFiberRoots(1) : null;if (renderers) {for (const renderer of renderers) {// 添加分析器if (hook.onCommitFiberRoot) {const originalOnCommitFiberRoot = hook.onCommitFiberRoot.bind(hook);hook.onCommitFiberRoot = (...args) => {const [, root] = args;try {this.processReactCommit(root);} catch (e) {console.error('[ReactMonitoring] Error in React profiling:', e);}// 调用原始方法return originalOnCommitFiberRoot(...args);};}}}}}processReactCommit(root) {// 这是一个简化版的实现// 实际上,从Fiber树提取性能数据很复杂,需要深入了解React内部工作原理try {const commitTime = performance.now();reporter.sendMetric({name: PERFORMANCE_METRICS.COMPONENT_RENDER_TIME,value: {commitTime,components: this.extractComponentInfo(root)}});} catch (e) {console.error('[ReactMonitoring] Failed to process React commit:', e);}}extractComponentInfo(root) {// 这是一个简化的实现// 在实际应用中,需要遍历Fiber树来提取组件信息return {timestamp: performance.now(),// 这里应该有更多组件特定的数据};}// 提供React组件和钩子getReactComponents() {return {ErrorBoundary,withErrorBoundary,withRenderTracking,useRenderTracking,};}// 清理和销毁监控系统destroy() {if (!this.initialized) {return;}// 清理错误处理if (errorHandler) {errorHandler.destroy();}// 清理API错误跟踪if (this.apiErrorTracker) {this.apiErrorTracker.restore();}// 清理数据上报if (reporter) {reporter.destroy();}if (dataTransport) {dataTransport.destroy();}this.initialized = false;console.log('[ReactMonitoring] System destroyed and cleaned up');}
}// 创建单例实例
export const reactMonitoring = new ReactMonitoring();// 导出React组件和钩子,方便直接使用
export const {ErrorBoundary,withErrorBoundary,withRenderTracking,useRenderTracking,
} = reactMonitoring.getReactComponents();// 默认导出监控系统实例
export default reactMonitoring;
应用实践
应用集成示例
// 在应用入口 index.js 中初始化
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import reactMonitoring from './monitoring';// 初始化监控系统
reactMonitoring.init({enablePerformanceMonitoring: true,enableErrorMonitoring: true,errorReportingEndpoint: 'https://api.example.com/monitoring/errors',metricsReportingEndpoint: 'https://api.example.com/monitoring/metrics',samplingRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0, // 生产环境采样10%,开发环境全采样
});// 使用全局错误边界包装应用
const MonitoredApp = () => (<reactMonitoring.ErrorBoundaryfallback={<div>应用出现了问题,正在尝试恢复...</div>}errorLevel="fatal"><App /></reactMonitoring.ErrorBoundary>
);ReactDOM.render(<MonitoredApp />, document.getElementById('root'));
组件级性能监控示例
// 使用HOC监控类组件
import React, { Component } from 'react';
import { withRenderTracking, withErrorBoundary } from './monitoring';class ExpensiveComponent extends Component {render() {return (<div>{/* 复杂组件逻辑 */}{this.props.items.map(item => (<div key={item.id}>{item.name}</div>))}</div>);}
}// 应用监控HOC
export default withErrorBoundary(withRenderTracking(ExpensiveComponent, {componentName: 'ExpensiveComponent',threshold: 8, // 8ms渲染警告阈值trackProps: true}), {fallback: <div>组件加载失败</div>,errorLevel: 'error'}
);// 使用Hook监控函数组件
import React, { useState } from 'react';
import { useRenderTracking } from './monitoring';function ExpensiveCounter(props) {// 监控组件渲染性能const renderCount = useRenderTracking('ExpensiveCounter', { threshold: 5 });const [count, setCount] = useState(0);// 模拟一个可能导致性能问题的操作const expensiveCalculation = () => {// 假设这是一个昂贵的计算let result = 0;for (let i = 0; i < 1000000; i++) {result += i;}return result;};const result = expensiveCalculation();return (<div><p>Count: {count}</p><p>Calculation: {result}</p><p>Render count: {renderCount}</p><button onClick={() => setCount(count + 1)}>Increment</button></div>);
}export default ExpensiveCounter;
监控数据可视化方案
// 监控仪表板组件
import React, { useState, useEffect } from 'react';
import { LineChart, BarChart, PieChart } from 'some-chart-library';
import { fetchMetricsData, fetchErrorData } from '../api';export function PerformanceDashboard() {const [metrics, setMetrics] = useState(null);const [errors, setErrors] = useState(null);const [timeRange, setTimeRange] = useState('24h');const [loading, setLoading] = useState(true);useEffect(() => {async function loadData() {setLoading(true);try {// 并行加载数据const [metricsData, errorsData] = await Promise.all([fetchMetricsData({ timeRange }),fetchErrorData({ timeRange })]);setMetrics(metricsData);setErrors(errorsData);} catch (err) {console.error('Failed to load monitoring data:', err);} finally {setLoading(false);}}loadData();}, [timeRange]);if (loading) {return <div>Loading dashboard data...</div>;}// 渲染性能指标图表return (<div className="monitoring-dashboard"><div className="dashboard-header"><h1>React Application Monitoring</h1><div className="time-selector"><button onClick={() => setTimeRange('1h')}>Last Hour</button><button onClick={() => setTimeRange('24h')}>Last 24 Hours</button><button onClick={() => setTimeRange('7d')}>Last 7 Days</button></div></div><div className="dashboard-section"><h2>Core Web Vitals</h2><div className="metrics-grid"><MetricCardtitle="LCP"value={metrics.lcp.median}threshold={2500}unit="ms"description="Largest Contentful Paint"/><MetricCardtitle="FID"value={metrics.fid.median}threshold={100}unit="ms"description="First Input Delay"/><MetricCardtitle="CLS"value={metrics.cls.median}threshold={0.1}unit=""description="Cumulative Layout Shift"/></div><h3>LCP Trend</h3><LineChartdata={metrics.lcp.history}xKey="timestamp"yKey="value"threshold={2500}/></div><div className="dashboard-section"><h2>Component Performance</h2><BarChartdata={metrics.componentRenderTime}xKey="componentName"yKey="avgDuration"sortBy="avgDuration"/></div><div className="dashboard-section"><h2>Error Distribution</h2><PieChartdata={errors.byType}valueKey="count"labelKey="type"/><h3>Recent Errors</h3><ErrorsTable errors={errors.recent} /></div></div>);
}// 单个指标卡片组件
function MetricCard({ title, value, threshold, unit, description }) {// 根据阈值确定状态const getStatus = () => {if (value <= threshold * 0.75) return 'good';if (value <= threshold) return 'warning';return 'poor';};const status = getStatus();return (<div className={`metric-card ${status}`}><div className="metric-title">{title}</div><div className="metric-value">{value.toFixed(2)}{unit}</div><div className="metric-description">{description}</div><div className="metric-threshold">{status === 'good' && '✓ Good'}{status === 'warning' && '⚠️ Needs Improvement'}{status === 'poor' && '✗ Poor'}</div></div>);
}// 错误表格组件
function ErrorsTable({ errors }) {return (<table className="errors-table"><thead><tr><th>Time</th><th>Type</th><th>Message</th><th>Component</th><th>Actions</th></tr></thead><tbody>{errors.map(error => (<tr key={error.id}><td>{new Date(error.timestamp).toLocaleString()}</td><td>{error.type}</td><td>{error.message}</td><td>{error.componentName || 'N/A'}</td><td><button onClick={() => viewErrorDetails(error.id)}>Details</button></td></tr>))}</tbody></table>);
}
性能优化建议与实施方案
基于监控收集的数据,我们可以制定针对性的优化策略:
-
组件懒加载与代码分割:根据页面加载性能数据,识别首屏加载瓶颈,将非关键组件延迟加载。
-
状态管理优化:利用渲染追踪数据,识别过度渲染的组件,应用
React.memo
、useMemo
和useCallback
降低不必要的重渲染。 -
虚拟化长列表:对于识别出渲染时间过长的列表组件,应用窗口化技术(react-window)仅渲染可视区域项目。
-
图片与资源优化:根据资源加载错误和性能数据,优化静态资源加载策略,应用懒加载与适当的分辨率。
-
错误预防机制:基于收集的错误模式构建更健壮的输入验证和错误恢复机制,提高应用稳定性。
总结与反思
构建完整的 React 性能监控与错误上报系统需要系统性地考虑数据采集、传输、存储和分析等环节。我们应该遵循以下原则:
-
低侵入性:通过 HOC 和 Hooks 模式,实现了对组件的无痛监控,不影响业务逻辑。
-
可扩展性:模块化设计使系统易于根据实际需求进行扩展和定制。
-
性能影响最小化:采样策略和批处理机制确保监控系统本身不会成为应用的性能负担。
-
数据安全与隐私:提供了匿名化和数据过滤机制,符合现代数据保护要求。
-
自动化分析:通过阈值检测和趋势分析,实现了问题的自动识别与预警。
在实际应用中,还应根据项目规模和需求选择合适的集成方式,从简单的单一指标监控开始,逐步扩展到全面的性能与错误追踪系统,以持续提升 React 应用的质量与用户体验。
参考资源
官方文档与规范
- React 性能优化文档 - React 官方性能优化指南
- Web Vitals - Google 定义的核心网页指标标准
- Performance API - MDN 关于浏览器 Performance API 的详细文档
- Error Boundaries - React 官方错误边界文档
- React Profiler API - React 内置性能分析 API 文档
监控工具与框架
- Sentry - 功能丰富的错误监控平台,提供 React SDK
- LogRocket - 会话重放和前端监控系统
- New Relic - 全栈性能监控解决方案
- Datadog RUM - 真实用户监控平台
开源库
- Web Vitals - 测量核心 Web 指标的小型库
- React Query - 包含自动错误处理功能的数据管理库
- React Error Boundary - 灵活的错误边界组件
- why-did-you-render - 检测不必要的组件重渲染
- use-error-boundary - 用于函数组件的错误边界 Hook
服务端集成
- OpenTelemetry - 开源可观测性框架,适用于分布式跟踪
- Elasticsearch + Kibana - 强大的日志分析和可视化工具
- Grafana - 开源指标分析与可视化平台
- Prometheus - 开源系统监控和告警工具
技术博客与最佳实践
- Netflix 技术博客: 性能监控 - Netflix 的前端性能监控实践
- Airbnb 的前端错误跟踪实践
- Facebook 工程博客: 前端性能
- 前端观察性工程实践
- React 性能:Stack Overflow 内部实践
行业标准与测量工具
- Lighthouse - 网站质量自动化审计工具
- WebPageTest - 免费网站性能测试工具
- Chrome DevTools Performance - 深入性能分析指南
- React Developer Tools Profiler - React 专用性能分析工具
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻
相关文章:
React 性能监控与错误上报
核心问题与技术挑战 现代 React 应用随着业务复杂度增加,性能问题和运行时错误日益成为影响用户体验的关键因素。没有可靠的监控与错误上报机制,我们将陷入被动修复而非主动预防的困境。 性能指标体系与错误分类 关键性能指标定义 // performance-me…...

AI 如何改变软件文档生产方式?
现代软件工程中的文档革命:从附属品到核心组件的范式升级 在数字化转型浪潮席卷全球的当下,软件系统的复杂度与规模呈现指数级增长。据Gartner最新研究显示,超过67%的企业软件项目延期或超预算的根本原因可追溯至文档系统的缺陷。这一现象在…...

激光干涉仪:解锁协作机器人DD马达的精度密码
在工业4.0的浪潮中,协作机器人正以惊人的灵活性重塑生产线——它们与工人并肩作业,精准搬运零件,完成精密装配。还能协同医生完成手术,甚至制作咖啡。 标准的协作机器人关节模组由角度编码器、直驱电机(DD马达)、驱动器、谐波减速…...
Windows如何定制键盘按键
Windows如何定制键盘按键 https://blog.csdn.net/qq_33204709/article/details/129010351...
go语言学习 第1章:走进Golang
第1章:走进Golang 一、Golang简介 Go语言(又称Golang)是由Google的Robert Griesemer、Rob Pike及Ken Thompson开发的一种开源编程语言。它诞生于2007年,2009年11月正式开源。Go语言的设计初衷是为了在不损失应用程序性能的情况下…...
使用Prometheus+Grafana+Alertmanager+Webhook-dingtalk搭建监控平台
一、监控平台介绍 1.监控平台简述普罗米修斯四件套,分别为Prometheus、Grafana、Alertmanager、Webhook-DingTalk。Prometheus一套开源的监控&报警&时间序列数据库的组合,由SoundCloud公司开发,广泛用于云原生环境和容器化应用的监控和性能分析。其提供了通用的数据…...

HOPE800系列变频器安装到快速调试的详细操作说明
以下是HOPE800系列变频器从安装到调试的详细操作说明及重要参数设置,适用于工程技术人员或具备电气基础的操作人员。请严格遵循安全规范操作。 以下面电机铭牌为例: HOPE800变频器安装与调试指南** (安全第一!操作前务必断电并确…...

vCenter与ESXi主机每分钟周期性断连修复
问题概述 最近我的测试服务器借给客户用作临时中转,仅更改了ESXi的管理IP,设备拿回来改回原来IP,vCenter开启后重新接收证书,主机和所有VM管理运行正常,跑着跑着发现主机和vCenter会频繁断开连接后又马上自动恢复&…...
JMeter函数整理
"_csvRead"函数 csvRead函数是从外部读取参数,csvRead函数可以从一个文件中读取多个参数。 下面具体讲一下如何使用csvread函数: 1.新建一个csv或者text文件,里面保存要读取的参数,每个参数间用逗号相隔。每行表示每一组…...
Sql Server 中常用语句
1.创建用户数据库 --创建数据库 use master --切换到master数据库 go-- 终止所有与SaleManagerDB数据库的连接 alter database SaleManagerDB set single_user with rollback immediate goif exists (select * from sysdatabases where nameSaleManagerDB) drop database Sal…...

web3-区块链困境破解指南:从数字化签名到Rollup 到分片
web3-区块链三难困境破解指南:从数字化签名到Rollup 到分片 数字化签名 实体的签名:将交易和签名者绑定在一起 在数字世界的问题是: 任何人都可以从任一文档复制Bob的签名放到自己想放的地方。 解决方案:让签名由文件来决定 b…...

李飞飞World Labs开源革命性Web端3D渲染器Forge!3D高斯溅射技术首次实现全平台流畅运行
在AI与3D技术深度融合的今天,李飞飞领衔的World Labs团队再次成为行业焦点。今日,他们正式开源了Forge——一款专为Web端设计的3D高斯溅射(3D Gaussian Splatting)渲染器,不仅支持THREE.js生态,更能在手机、…...

小鹏汽车5月交付新车33525台 同比增长230%
6月1日,小鹏汽车公布5月交付数据,5月小鹏交付新车33,525台,同比增长230%,与4月交付35,045台相比下降4.3%,已连续7个月交付量突破30,000台。2025年1-5月,小鹏汽车累计交付新车162,578台,同比增长…...

OpenCV——Mat类及常用数据结构
Mat类及常用数据结构 一、Mat类简介1.1、矩阵头1.2、矩阵的数据类型1.3、Mat的子类 二、矩阵数据的存储2.1、单通道2.2、多通道 三、创建矩阵的方法3.1、静态方法创建3.2、构造方法创建3.3、读取图像文件创建3.4、克隆创建 四、获取矩阵信息五、矩阵相关操作5.1、获取/修改像素…...
深入解析FutureTask:原理与实战
我们来深入解析 FutureTask。下面将从它们的用法开始,逐步深入到底层实现、方法和接口设计,并探讨它们对于我们自己设计实现以及其他编程实践的学习意义。 主要就是放入等待队列(CAS操作一个链表头),完成任务的线程唤…...

每天总结一个html标签——Audio音频标签
Audio标签 文章目录 Audio标签一、audio标签的定义与介绍1. 定义介绍2. 语法3. 支持的格式4.文本提示 二、audio标签的HTML属性1. autoplay2. loop3. muted4. preload 三、audio标签的常用DOM属性四、audio标签的常用事件四、默认样式五、自定义样式1. 示例2. 代码 六、播放 m3…...
使用 React Native 开发鸿蒙(HarmonyOS)运动健康类应用的系统化准备工作
⚙️ 一、环境与工具准备 双环境搭建 React Native 环境:安装 Node.js(≥18.x)、JDK(≥11)、Yarn。鸿蒙开发环境: 下载 DevEco Studio 4.0 及 HarmonyOS SDK;配…...

web3-Remix部署智能合约到“荷兰式”拍卖及以太坊gas费机制细讲
web3-Remix部署智能合约到“荷兰式”拍卖及以太坊gas费机制细讲 一、使用Remix演示智能合约部署 智能合约的代码编写一般都是在Remix上,Remix的好处的话就是可以在浏览器中快速开发和部署合约,无需在本地安装任何程序,十分适合新手。 对应…...

网络编程及原理(一)
目录 一 . 独立模式与网络互联 二 . 局域网 —— LAN (1)基于网线直连 (2)基于集线器组建 (3)基于交换机组建 (4)基于交换机和路由器组建 三 . 广域网 —— WAN 四 …...
superior哥AI系列第9期:高效训练与部署:从实验室到生产环境
🚀 superior哥AI系列第9期:高效训练与部署:从实验室到生产环境 嘿!小伙伴们!👋 欢迎来到superior哥AI系列第9期!经过前面8期的学习,你已经掌握了深度学习的核心技术。但是࿰…...

【Linux】进程 信号保存 信号处理 OS用户态/内核态
🌻个人主页:路飞雪吖~ 🌠专栏:Linux 目录 一、信号保存 ✨进程如何完成对信号的保存? ✨在内核中的表示 ✨sigset_t ✨信号操作函数 🪄sigprocmask --- 获取或设置当前进程的 block表 🪄s…...

[ Qt ] | 与系统相关的操作(一):鼠标相关事件
目录 信号和事件的关系 (leaveEvent和enterEvent) 实现通过事件获取鼠标进入和鼠标离开 (mousePressEvent) 实现通过事件获得鼠标点击的位置 (mouseReleaseEvent) 前一个的基础上添加鼠标释放事件 (mouseDoubleClickEvent) 鼠标双击事件 鼠标移动事件 鼠标滚轮事件 …...

stm32使用hal库模拟spi模式3
因为网上模拟spi模拟的都是模式0,很少有模式3的。 模式3的时序图,在clk的下降沿切换电平状态,在上升沿采样, SCK空闲为高电平 初始化cs,clk,miso,mosi四个io。miso配置为输入,cs、c…...
安装 Nginx
个人博客地址:安装 Nginx | 一张假钞的真实世界 对于 Linux 平台,Nginx 安装包 可以从 nginx.org 下载。 Ubuntu: 版本Codename支持平台12.04precisex86_64, i38614.04trustyx86_64, i386, aarch64/arm6415.10wilyx86_64, i386 在 Debian/Ubuntu 系统…...
Vue-1-前端框架Vue基础入门之一
文章目录 1 Vue简介1.1 Vue的特性1.2 Vue的版本2 Vue的基础应用2.1 Vue3的下载2.2 Vue3的新语法2.3 vue-devtools调试工具3 Vue的指令3.1 内容渲染指令{{}}3.2 属性绑定指令v-bind3.3 事件绑定指令v-on3.4 双向绑定指令v-model3.5 条件渲染指令v-if3.6 列表渲染指令v-for4 参考…...

OurBMC技术委员会2025年二季度例会顺利召开
5月28日,OurBMC社区技术委员会二季度例会顺利召开。本次会议采用线上线下结合的方式,各委员在会上听取了OurBMC社区二季度工作总结汇报,规划了2025年三季度的重点工作。 会上,技术委员会主席李煜汇报了社区2025年二季度主要工作及…...

postman自动化测试
目录 一、相关知识 1.网络协议 2.接口测试 3.编写测试用例 4.系统架构 二、如何请求 1.get请求 编辑2.post请求 3.用环境变量请求 4.Postman测试沙箱 一、相关知识 1.网络协议 规定数据信息发送与解析的方式。 网络传输协议 https相比http,信息在网…...

力扣热题100之二叉树的直径
题目 给你一棵二叉树的根节点,返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 代码 方法:递归 计算二叉树的直径可以理解…...

数字人技术的核心:AI与动作捕捉的双引擎驱动(210)
**摘要:**数字人技术从静态建模迈向动态交互,AI与动作捕捉技术的深度融合推动其智能化发展。尽管面临表情僵硬、动作脱节、交互机械等技术瓶颈,但通过多模态融合技术、轻量化动捕方案等创新,数字人正逐步实现自然交互与情感表达。…...
c++ 命名规则
目录 总结1. 类名(Class Names)2. 变量名(Variable Names)3. 函数名(Function Names)4. 宏定义(Macros)5. 命名空间(Namespaces)6. 枚举(Enums&am…...