# 设计文档 ## 概述 本设计文档针对 Argon WordPress 主题的资源使用和 CPU 占用问题,提供系统性的优化方案。通过分析主题核心代码(argontheme.js ~3700 行、style.css ~12000 行),识别出以下主要性能瓶颈: **关键问题:** 1. 滚动和 resize 事件处理器未使用节流/防抖,导致高频执行 2. DOM 元素重复查询,缺少缓存机制 3. 事件监听器未正确清理,存在内存泄漏风险 4. 第三方库全量加载,未按需加载 5. CSS 选择器复杂度高,样式计算耗时 6. 动画未充分利用 GPU 加速 **优化策略:** - 实现事件节流和防抖机制 - 建立 DOM 缓存系统 - 完善事件监听器生命周期管理 - 实现第三方库按需加载 - 优化 CSS 选择器和动画性能 - 添加性能监控和分析工具 本设计与现有的 `global-ui-optimization` 和 `pjax-lazyload-optimization` 互补,专注于底层性能优化。 ## 架构 ### 整体架构 ``` ┌─────────────────────────────────────────────────────────────┐ │ Argon 主题性能优化层 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ DOM 缓存层 │ │ 事件管理层 │ │ 资源加载层 │ │ │ │ │ │ │ │ │ │ │ │ - 元素缓存 │ │ - 节流/防抖 │ │ - 按需加载 │ │ │ │ - 查询优化 │ │ - 监听器管理 │ │ - 预加载策略 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 渲染优化层 │ │ 内存管理层 │ │ 监控分析层 │ │ │ │ │ │ │ │ │ │ │ │ - GPU 加速 │ │ - 清理机制 │ │ - 性能指标 │ │ │ │ - 布局优化 │ │ - 引用管理 │ │ - 问题检测 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 现有主题代码 │ │ argontheme.js │ style.css │ functions.php │ └─────────────────────────────────────────────────────────────┘ ``` ### 模块划分 **1. DOM 缓存模块 (ArgonDOMCache)** - 缓存频繁访问的 DOM 元素 - 提供统一的查询接口 - PJAX 页面切换时自动更新缓存 **2. 事件管理模块 (ArgonEventManager)** - 提供节流和防抖工具函数 - 管理事件监听器的生命周期 - 支持批量清理和重新绑定 **3. 资源加载模块 (ArgonResourceLoader)** - 按需加载第三方库 - 实现资源预加载策略 - 管理加载状态和缓存 **4. 渲染优化模块 (ArgonRenderOptimizer)** - 批量读写 DOM 避免布局抖动 - 管理 GPU 加速属性 - 优化动画性能 **5. 内存管理模块 (ArgonMemoryManager)** - 清理事件监听器 - 管理闭包引用 - 清理定时器和动画帧 **6. 性能监控模块 (ArgonPerformanceMonitor)** - 记录关键性能指标 - 检测性能问题 - 提供优化建议 ## 组件和接口 ### 1. DOM 缓存模块 **ArgonDOMCache 类** ```javascript class ArgonDOMCache { constructor() { this.cache = new Map(); this.initialized = false; } /** * 初始化缓存 */ init() { this.cache.clear(); // 缓存常用元素 this.set('toolbar', document.querySelector('.navbar')); this.set('content', document.getElementById('content')); this.set('leftbar', document.getElementById('leftbar')); this.set('sidebar', document.getElementById('sidebar')); this.set('backToTopBtn', document.getElementById('fabtn_back_to_top')); this.set('readingProgress', document.getElementById('fabtn_reading_progress')); this.set('comments', document.getElementById('comments')); this.set('postComment', document.getElementById('post_comment')); this.initialized = true; } /** * 获取缓存的元素 * @param {string} key - 元素键名 * @returns {Element|null} */ get(key) { if (!this.cache.has(key)) { return null; } return this.cache.get(key); } /** * 设置缓存 * @param {string} key - 元素键名 * @param {Element} element - DOM 元素 */ set(key, element) { this.cache.set(key, element); } /** * 清空缓存 */ clear() { this.cache.clear(); this.initialized = false; } } ``` **使用示例:** ```javascript // 初始化 const domCache = new ArgonDOMCache(); domCache.init(); // 使用缓存而非重复查询 const toolbar = domCache.get('toolbar'); if (toolbar) { toolbar.style.opacity = '0.8'; } // PJAX 页面切换时重新初始化 $(document).on('pjax:end', function() { domCache.init(); }); ``` ### 2. 事件管理模块 **ArgonEventManager 类** ```javascript class ArgonEventManager { constructor() { this.listeners = new Map(); } /** * 节流函数 * @param {Function} func - 要节流的函数 * @param {number} wait - 等待时间(毫秒) * @returns {Function} */ throttle(func, wait = 16) { let lastTime = 0; let timeoutId = null; return function(...args) { const now = Date.now(); const remaining = wait - (now - lastTime); if (remaining <= 0) { if (timeoutId) { cancelAnimationFrame(timeoutId); timeoutId = null; } lastTime = now; func.apply(this, args); } else if (!timeoutId) { timeoutId = requestAnimationFrame(() => { lastTime = Date.now(); timeoutId = null; func.apply(this, args); }); } }; } /** * 防抖函数 * @param {Function} func - 要防抖的函数 * @param {number} wait - 等待时间(毫秒) * @returns {Function} */ debounce(func, wait = 150) { let timeoutId = null; return function(...args) { if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { func.apply(this, args); }, wait); }; } /** * 添加事件监听器 * @param {Element} element - DOM 元素 * @param {string} event - 事件名称 * @param {Function} handler - 处理函数 * @param {Object} options - 选项 */ on(element, event, handler, options = {}) { if (!element) return; const key = this._getKey(element, event); element.addEventListener(event, handler, options); if (!this.listeners.has(key)) { this.listeners.set(key, []); } this.listeners.get(key).push({ handler, options }); } /** * 移除事件监听器 * @param {Element} element - DOM 元素 * @param {string} event - 事件名称 */ off(element, event) { if (!element) return; const key = this._getKey(element, event); const handlers = this.listeners.get(key); if (handlers) { handlers.forEach(({ handler, options }) => { element.removeEventListener(event, handler, options); }); this.listeners.delete(key); } } /** * 清除所有事件监听器 */ clear() { this.listeners.forEach((handlers, key) => { const [element, event] = this._parseKey(key); handlers.forEach(({ handler, options }) => { element.removeEventListener(event, handler, options); }); }); this.listeners.clear(); } _getKey(element, event) { return `${element}_${event}`; } _parseKey(key) { const parts = key.split('_'); return [parts[0], parts[1]]; } } ``` **使用示例:** ```javascript const eventManager = new ArgonEventManager(); // 使用节流处理滚动事件 const handleScroll = eventManager.throttle(() => { const scrollTop = document.documentElement.scrollTop; // 处理滚动逻辑 }, 16); eventManager.on(document, 'scroll', handleScroll, { passive: true }); // 使用防抖处理 resize 事件 const handleResize = eventManager.debounce(() => { // 处理 resize 逻辑 }, 150); eventManager.on(window, 'resize', handleResize); // PJAX 页面切换时清理 $(document).on('pjax:beforeReplace', function() { eventManager.clear(); }); ``` ### 3. 资源加载模块 **ArgonResourceLoader 类** ```javascript class ArgonResourceLoader { constructor() { this.loaded = new Set(); this.loading = new Map(); } /** * 按需加载脚本 * @param {string} name - 资源名称 * @param {string} url - 资源 URL * @returns {Promise} */ async loadScript(name, url) { if (this.loaded.has(name)) { return Promise.resolve(); } if (this.loading.has(name)) { return this.loading.get(name); } const promise = new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = url; script.async = true; script.onload = () => { this.loaded.add(name); this.loading.delete(name); resolve(); }; script.onerror = () => { this.loading.delete(name); reject(new Error(`Failed to load ${name}`)); }; document.head.appendChild(script); }); this.loading.set(name, promise); return promise; } /** * 检查页面是否需要某个库 * @param {string} selector - CSS 选择器 * @returns {boolean} */ needsLibrary(selector) { return document.querySelector(selector) !== null; } /** * 条件加载 Prism 代码高亮 */ async loadPrismIfNeeded() { if (!this.needsLibrary('pre code')) { return; } if (typeof window.Prism !== 'undefined' && window.Prism.highlightAll) { return; } await this.loadScript('prism', argonConfig.prism_url); } /** * 条件加载 Zoomify 图片放大 */ async loadZoomifyIfNeeded() { if (!this.needsLibrary('.post-content img')) { return; } if (typeof window.Zoomify !== 'undefined') { return; } await this.loadScript('zoomify', argonConfig.zoomify_url); } /** * 条件加载 Tippy 提示框 */ async loadTippyIfNeeded() { if (!this.needsLibrary('[data-tippy-content]')) { return; } if (typeof window.tippy !== 'undefined') { return; } await this.loadScript('tippy', argonConfig.tippy_url); } } ``` ### 4. 渲染优化模块 **ArgonRenderOptimizer 类** ```javascript class ArgonRenderOptimizer { constructor() { this.readQueue = []; this.writeQueue = []; this.scheduled = false; } /** * 批量读取 DOM 属性 * @param {Function} readFn - 读取函数 */ read(readFn) { this.readQueue.push(readFn); this._schedule(); } /** * 批量写入 DOM * @param {Function} writeFn - 写入函数 */ write(writeFn) { this.writeQueue.push(writeFn); this._schedule(); } /** * 调度执行 */ _schedule() { if (this.scheduled) return; this.scheduled = true; requestAnimationFrame(() => { this._flush(); }); } /** * 执行队列 */ _flush() { // 先执行所有读取操作 while (this.readQueue.length) { const readFn = this.readQueue.shift(); readFn(); } // 再执行所有写入操作 while (this.writeQueue.length) { const writeFn = this.writeQueue.shift(); writeFn(); } this.scheduled = false; } /** * 添加 GPU 加速提示 * @param {Element} element - DOM 元素 */ enableGPU(element) { if (!element) return; element.style.willChange = 'transform, opacity'; } /** * 移除 GPU 加速提示 * @param {Element} element - DOM 元素 */ disableGPU(element) { if (!element) return; element.style.willChange = 'auto'; } } ``` **使用示例:** ```javascript const renderOptimizer = new ArgonRenderOptimizer(); // 批量读写避免布局抖动 let scrollTop, windowHeight; renderOptimizer.read(() => { scrollTop = document.documentElement.scrollTop; windowHeight = window.innerHeight; }); renderOptimizer.write(() => { const toolbar = domCache.get('toolbar'); if (toolbar) { toolbar.style.opacity = scrollTop > 100 ? '1' : '0.8'; } }); // 动画前启用 GPU 加速 const element = document.querySelector('.animated-element'); renderOptimizer.enableGPU(element); // 动画完成后禁用 element.addEventListener('animationend', () => { renderOptimizer.disableGPU(element); }); ``` ### 5. 内存管理模块 **ArgonMemoryManager 类** ```javascript class ArgonMemoryManager { constructor() { this.timers = new Set(); this.frames = new Set(); this.intervals = new Set(); } /** * 设置定时器并跟踪 * @param {Function} callback - 回调函数 * @param {number} delay - 延迟时间 * @returns {number} timer ID */ setTimeout(callback, delay) { const id = setTimeout(() => { this.timers.delete(id); callback(); }, delay); this.timers.add(id); return id; } /** * 设置间隔定时器并跟踪 * @param {Function} callback - 回调函数 * @param {number} interval - 间隔时间 * @returns {number} interval ID */ setInterval(callback, interval) { const id = setInterval(callback, interval); this.intervals.add(id); return id; } /** * 请求动画帧并跟踪 * @param {Function} callback - 回调函数 * @returns {number} frame ID */ requestAnimationFrame(callback) { const id = requestAnimationFrame(() => { this.frames.delete(id); callback(); }); this.frames.add(id); return id; } /** * 清除定时器 * @param {number} id - timer ID */ clearTimeout(id) { clearTimeout(id); this.timers.delete(id); } /** * 清除间隔定时器 * @param {number} id - interval ID */ clearInterval(id) { clearInterval(id); this.intervals.delete(id); } /** * 取消动画帧 * @param {number} id - frame ID */ cancelAnimationFrame(id) { cancelAnimationFrame(id); this.frames.delete(id); } /** * 清理所有定时器和动画帧 */ clearAll() { this.timers.forEach(id => clearTimeout(id)); this.intervals.forEach(id => clearInterval(id)); this.frames.forEach(id => cancelAnimationFrame(id)); this.timers.clear(); this.intervals.clear(); this.frames.clear(); } } ``` ### 6. 性能监控模块 **ArgonPerformanceMonitor 类** ```javascript class ArgonPerformanceMonitor { constructor() { this.metrics = {}; this.enabled = argonConfig.debug_mode || false; } /** * 记录性能指标 */ recordMetrics() { if (!this.enabled || !window.performance) return; const perfData = performance.getEntriesByType('navigation')[0]; if (!perfData) return; this.metrics = { dns: perfData.domainLookupEnd - perfData.domainLookupStart, tcp: perfData.connectEnd - perfData.connectStart, request: perfData.responseStart - perfData.requestStart, response: perfData.responseEnd - perfData.responseStart, dom: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart, load: perfData.loadEventEnd - perfData.loadEventStart, total: perfData.loadEventEnd - perfData.fetchStart }; } /** * 检测性能问题 */ detectIssues() { if (!this.enabled) return; const issues = []; // 检查 DOM 查询频率 if (this._getDOMQueryCount() > 100) { issues.push('警告:DOM 查询次数过多,建议使用缓存'); } // 检查事件监听器数量 if (this._getEventListenerCount() > 50) { issues.push('警告:事件监听器数量过多,可能存在内存泄漏'); } // 检查长任务 if (this._hasLongTasks()) { issues.push('警告:检测到长任务,可能阻塞主线程'); } if (issues.length > 0) { console.warn('Argon 性能问题:', issues); } } /** * 输出性能报告 */ report() { if (!this.enabled) return; console.group('Argon 性能报告'); console.table(this.metrics); this.detectIssues(); console.groupEnd(); } _getDOMQueryCount() { // 简化实现,实际需要更复杂的跟踪 return 0; } _getEventListenerCount() { // 简化实现,实际需要更复杂的跟踪 return 0; } _hasLongTasks() { // 简化实现,实际需要使用 PerformanceObserver return false; } } ``` ## 数据模型 ### 性能配置对象 ```javascript const ArgonPerformanceConfig = { // 事件节流配置 throttle: { scroll: 16, // 滚动事件节流间隔(毫秒) resize: 16, // resize 事件节流间隔(毫秒) mousemove: 16 // 鼠标移动事件节流间隔(毫秒) }, // 事件防抖配置 debounce: { resize: 150, // resize 事件防抖延迟(毫秒) input: 300, // 输入事件防抖延迟(毫秒) search: 500 // 搜索事件防抖延迟(毫秒) }, // 资源加载配置 lazyLoad: { prism: true, // 按需加载 Prism zoomify: true, // 按需加载 Zoomify tippy: true // 按需加载 Tippy }, // 缓存配置 cache: { maxSize: 100, // 最大缓存数量 ttl: 300000 // 缓存过期时间(毫秒) }, // 性能监控配置 monitor: { enabled: false, // 是否启用监控 reportInterval: 60000 // 报告间隔(毫秒) } }; ``` ### DOM 缓存数据结构 ```javascript // Map 结构存储 DOM 元素缓存 const domCacheStructure = { // key: 元素标识符(字符串) // value: DOM 元素引用 'toolbar': HTMLElement, 'content': HTMLElement, 'leftbar': HTMLElement, 'sidebar': HTMLElement, 'backToTopBtn': HTMLElement, 'readingProgress': HTMLElement, 'comments': HTMLElement, 'postComment': HTMLElement }; ``` ### 事件监听器注册表 ```javascript // Map 结构存储事件监听器 const eventListenersStructure = { // key: 'element_eventType' 格式的字符串 // value: 监听器数组 'document_scroll': [ { handler: Function, options: { passive: true } } ], 'window_resize': [ { handler: Function, options: {} } ] }; ``` ### 资源加载状态 ```javascript const resourceLoadingState = { // 已加载的资源集合 loaded: Set(['jquery', 'bootstrap']), // 正在加载的资源 Map loading: Map([ ['prism', Promise], ['zoomify', Promise] ]) }; ``` ### 性能指标数据 ```javascript const performanceMetrics = { dns: 50, // DNS 查询时间(毫秒) tcp: 100, // TCP 连接时间(毫秒) request: 200, // 请求时间(毫秒) response: 300, // 响应时间(毫秒) dom: 500, // DOM 解析时间(毫秒) load: 100, // 加载事件时间(毫秒) total: 1250, // 总加载时间(毫秒) // 自定义指标 domQueries: 45, // DOM 查询次数 eventListeners: 23, // 事件监听器数量 memoryUsage: 50.5 // 内存使用(MB) }; ``` ## 错误处理 ### 错误类型 **1. 资源加载失败** - 场景:第三方库加载失败 - 处理:降级到基础功能,记录错误日志 - 示例:Prism 加载失败时,代码块仍然显示但无高亮 **2. DOM 元素不存在** - 场景:缓存的元素在 PJAX 后不存在 - 处理:返回 null,调用方检查后跳过操作 - 示例:评论区元素不存在时,不显示"前往评论"按钮 **3. 事件监听器清理失败** - 场景:尝试移除不存在的监听器 - 处理:静默失败,不影响其他清理操作 - 示例:PJAX 清理时某个监听器已被移除 **4. 性能监控 API 不可用** - 场景:旧浏览器不支持 Performance API - 处理:禁用监控功能,不影响主要功能 - 示例:IE11 中性能监控自动禁用 ### 错误处理策略 ```javascript // 资源加载错误处理 async function loadResourceSafely(name, url) { try { await resourceLoader.loadScript(name, url); } catch (error) { console.warn(`Failed to load ${name}:`, error); // 降级处理 if (name === 'prism') { // 代码块仍然显示,只是没有高亮 document.querySelectorAll('pre code').forEach(el => { el.classList.add('no-highlight'); }); } } } // DOM 操作错误处理 function updateElementSafely(element, property, value) { if (!element) { console.warn('Element not found, skipping update'); return; } try { element.style[property] = value; } catch (error) { console.error('Failed to update element:', error); } } // 事件清理错误处理 function clearEventsSafely() { try { eventManager.clear(); } catch (error) { console.error('Failed to clear events:', error); // 尝试手动清理关键监听器 try { document.removeEventListener('scroll', handleScroll); window.removeEventListener('resize', handleResize); } catch (e) { // 静默失败 } } } // 性能监控错误处理 function monitorPerformanceSafely() { if (!window.performance || !window.performance.getEntriesByType) { console.info('Performance API not available, monitoring disabled'); return; } try { performanceMonitor.recordMetrics(); } catch (error) { console.warn('Performance monitoring failed:', error); } } ``` ### 降级方案 **1. 节流/防抖降级** - 如果 requestAnimationFrame 不可用,使用 setTimeout - 如果 Date.now 不可用,使用 new Date().getTime() **2. 缓存降级** - 如果 Map 不可用,使用普通对象 - 如果 Set 不可用,使用数组 **3. 资源加载降级** - 如果动态加载失败,使用静态引入 - 如果 Promise 不可用,使用回调函数 **4. 性能监控降级** - 如果 Performance API 不可用,禁用监控 - 如果 PerformanceObserver 不可用,使用简化版监控 ## 测试策略 ### 测试方法 本优化项目采用双重测试策略: **1. 单元测试** - 测试具体的优化功能实现 - 验证边界条件和错误处理 - 测试特定场景的行为 **2. 属性测试** - 验证优化机制的通用性质 - 使用随机生成的输入测试 - 确保优化在各种情况下都有效 **3. 性能测试** - 测量优化前后的性能指标 - 验证 CPU 占用降低 - 确认内存使用优化 ### 测试工具 **JavaScript 测试框架:** - Jest 或 Mocha - 单元测试框架 - fast-check - 属性测试库 - Lighthouse - 性能测试工具 **性能监控工具:** - Chrome DevTools Performance - Performance API - Memory Profiler ### 测试配置 **属性测试配置:** - 每个属性测试至少运行 100 次迭代 - 使用随机生成器创建测试数据 - 每个测试标注对应的设计属性 **标注格式:** ```javascript // Feature: resource-cpu-optimization, Property 1: DOM 缓存初始化 test('DOM cache initializes all frequent elements', () => { // 测试代码 }); ``` ## 正确性属性 *属性是一个特征或行为,应该在系统的所有有效执行中保持为真——本质上是关于系统应该做什么的形式化陈述。属性作为人类可读规范和机器可验证正确性保证之间的桥梁。* ### 属性反思 在编写正确性属性之前,我对 prework 分析进行了反思,识别出以下可以合并或优化的属性: **合并的属性:** 1. 属性 7.1、7.2、7.3(按需加载 Prism、Zoomify、Tippy)可以合并为一个通用的"按需加载"属性 2. 属性 11.2 和 11.4(组件销毁和 PJAX 清理事件监听器)可以合并为一个"事件监听器清理"属性 3. 属性 19.3 和 19.5(交互时加载和避免重复加载)可以合并为一个"模块按需加载"属性 **移除的冗余属性:** - 属性 1.2(优先使用缓存)被属性 1.1(缓存初始化)隐含包含 - 属性 3.2(取消待执行任务)是防抖机制的核心,已被属性 3.1 包含 经过反思,最终保留 20 个独立且有价值的正确性属性。 ### 属性 1: DOM 缓存初始化完整性 *对于任意* 页面结构,当初始化 DOM 缓存时,所有预定义的频繁访问元素(如果存在)都应该被正确缓存 **验证:需求 1.1** ### 属性 2: DOM 缓存 PJAX 重置 *对于任意* PJAX 页面切换,缓存应该被清空并使用新页面的元素重新初始化 **验证:需求 1.3** ### 属性 3: 滚动事件节流频率限制 *对于任意* 快速连续的滚动事件序列,节流后的处理器执行频率不应超过每 16ms 一次 **验证:需求 2.1** ### 属性 4: 事件监听器清理完整性 *对于任意* 已注册的事件监听器集合,当触发清理时,所有监听器都应该被正确移除 **验证:需求 2.5, 11.2, 11.4** ### 属性 5: Resize 事件防抖延迟 *对于任意* 快速连续的 resize 事件序列,防抖后的处理器应该只在事件停止 150ms 后执行一次 **验证:需求 3.1, 3.2** ### 属性 6: GPU 加速生命周期 *对于任意* 需要动画的元素,will-change 属性应该在动画开始时设置,在动画结束时移除 **验证:需求 5.2, 5.3** ### 属性 7: 同时运行动画数量限制 *对于任意* 动画启动请求序列,系统应该限制同时运行的动画数量不超过 3 个 **验证:需求 5.5** ### 属性 8: 第三方库按需加载 *对于任意* 第三方库(Prism、Zoomify、Tippy),当页面不包含对应功能的元素时,该库不应该被加载 **验证:需求 7.1, 7.2, 7.3** ### 属性 9: 第三方库加载缓存 *对于任意* 第三方库,多次请求加载时应该只实际加载一次,后续请求使用缓存 **验证:需求 7.5** ### 属性 10: 定时器和动画帧清理 *对于任意* 创建的定时器和动画帧集合,当触发清理时,所有待执行的回调都应该被取消 **验证:需求 12.5, 13.4** ### 属性 11: 缓存大小上限 *对于任意* 缓存系统,当添加的数据超过设定的上限时,应该淘汰旧数据保持在上限内 **验证:需求 14.3** ### 属性 12: LRU 缓存淘汰策略 *对于任意* 缓存数据访问序列,当缓存满时,应该淘汰最少最近使用的数据 **验证:需求 14.4** ### 属性 13: 响应式图片尺寸选择 *对于任意* 设备像素比,系统应该加载与该像素比匹配的合适尺寸图片 **验证:需求 15.1** ### 属性 14: WebP 格式优先级 *对于任意* 支持 WebP 的浏览器,当图片有多种格式可用时,应该优先加载 WebP 格式 **验证:需求 15.2** ### 属性 15: 模块按需加载和缓存 *对于任意* 功能模块,应该在用户交互触发时才加载,且多次触发时不应重复加载 **验证:需求 19.3, 19.5** ### 边界情况和示例测试 以下是需要通过单元测试验证的特定场景: **示例 1: 不存在的元素返回 null** - 场景:请求缓存中不存在的元素 - 预期:返回 null 而不抛出异常 - **验证:需求 1.5** **示例 2: 移动端方向改变延迟** - 场景:移动设备方向改变 - 预期:布局调整延迟 300ms 执行 - **验证:需求 3.5** **示例 3: 字体加载失败降级** - 场景:自定义字体加载失败 - 预期:优雅降级到系统字体 - **验证:需求 8.3** **示例 4: 性能指标记录** - 场景:页面加载完成 - 预期:使用 Performance API 记录关键指标 - **验证:需求 18.1** **示例 5: 性能问题检测警告** - 场景:检测到性能问题(如过多 DOM 查询) - 预期:在控制台输出警告信息 - **验证:需求 18.2** **示例 6: 开发模式详细数据** - 场景:启用开发模式 - 预期:提供详细的性能分析数据 - **验证:需求 18.3** **示例 7: 生产模式精简数据** - 场景:启用生产模式 - 预期:仅记录关键指标 - **验证:需求 18.4** **示例 8: 性能异常优化建议** - 场景:性能指标异常 - 预期:提供针对性的优化建议 - **验证:需求 18.5** **示例 9: 模块加载失败降级** - 场景:功能模块加载失败 - 预期:提供降级方案或友好提示 - **验证:需求 19.4** ### 测试实现指南 **属性测试示例:** ```javascript // 使用 fast-check 进行属性测试 const fc = require('fast-check'); // Feature: resource-cpu-optimization, Property 3: 滚动事件节流频率限制 test('scroll event throttle limits execution frequency', () => { fc.assert( fc.property( fc.array(fc.integer({ min: 0, max: 1000 }), { minLength: 10, maxLength: 100 }), (timestamps) => { const eventManager = new ArgonEventManager(); const executions = []; const handler = eventManager.throttle(() => { executions.push(Date.now()); }, 16); // 模拟快速滚动事件 timestamps.forEach(ts => { setTimeout(handler, ts); }); // 等待所有事件处理完成 return new Promise(resolve => { setTimeout(() => { // 验证执行间隔不小于 16ms for (let i = 1; i < executions.length; i++) { expect(executions[i] - executions[i-1]).toBeGreaterThanOrEqual(16); } resolve(); }, 2000); }); } ), { numRuns: 100 } ); }); ``` **单元测试示例:** ```javascript // Feature: resource-cpu-optimization, Example 1: 不存在的元素返回 null test('cache returns null for non-existent elements', () => { const cache = new ArgonDOMCache(); cache.init(); const result = cache.get('non-existent-element'); expect(result).toBeNull(); expect(() => cache.get('non-existent-element')).not.toThrow(); }); ``` ### 性能基准测试 除了功能正确性测试,还需要进行性能基准测试: **测试指标:** 1. 滚动事件处理器 CPU 占用(优化前 vs 优化后) 2. Resize 事件处理器执行时间 3. DOM 查询次数和耗时 4. 内存使用量 5. 页面加载时间 6. 首次内容绘制(FCP)时间 7. 最大内容绘制(LCP)时间 **性能目标:** - 滚动时 CPU 占用降低 50% - DOM 查询次数减少 70% - 内存泄漏为 0 - 页面加载时间减少 20% - FCP 和 LCP 时间各减少 15%