2026-01-21 14:36:49 +08:00
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
// Argon 主题性能优化模块
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
// 本模块提供系统性的性能优化功能,包括:
|
|
|
|
|
|
// - DOM 缓存系统
|
|
|
|
|
|
// - 事件节流和防抖
|
|
|
|
|
|
// - 资源按需加载
|
|
|
|
|
|
// - 渲染优化
|
|
|
|
|
|
// - 内存管理
|
|
|
|
|
|
// - 性能监控
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 全局性能配置对象
|
|
|
|
|
|
* 包含所有性能优化相关的配置参数
|
|
|
|
|
|
*/
|
|
|
|
|
|
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, // 是否启用监控(默认关闭,可通过 argonConfig.debug_mode 启用)
|
|
|
|
|
|
reportInterval: 60000 // 报告间隔(毫秒)
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
// DOM 缓存模块
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* DOM 缓存类
|
|
|
|
|
|
* 缓存频繁访问的 DOM 元素,避免重复查询,提升性能
|
|
|
|
|
|
*
|
|
|
|
|
|
* @class ArgonDOMCache
|
|
|
|
|
|
*/
|
|
|
|
|
|
class ArgonDOMCache {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构造函数
|
|
|
|
|
|
* 初始化缓存存储结构
|
|
|
|
|
|
*/
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.cache = new Map();
|
|
|
|
|
|
this.initialized = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化缓存
|
|
|
|
|
|
* 缓存所有频繁访问的 DOM 元素
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
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} DOM 元素或 null(如果不存在)
|
|
|
|
|
|
*/
|
|
|
|
|
|
get(key) {
|
|
|
|
|
|
if (!this.cache.has(key)) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.cache.get(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置缓存
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {string} key - 元素键名
|
|
|
|
|
|
* @param {Element|null} element - DOM 元素
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
set(key, element) {
|
|
|
|
|
|
this.cache.set(key, element);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清空缓存
|
|
|
|
|
|
* 通常在 PJAX 页面切换时调用
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
clear() {
|
|
|
|
|
|
this.cache.clear();
|
|
|
|
|
|
this.initialized = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
// 事件管理模块
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 事件管理类
|
|
|
|
|
|
* 提供事件节流、防抖和监听器生命周期管理功能
|
|
|
|
|
|
*
|
|
|
|
|
|
* @class ArgonEventManager
|
|
|
|
|
|
*/
|
|
|
|
|
|
class ArgonEventManager {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构造函数
|
|
|
|
|
|
* 初始化监听器注册表
|
|
|
|
|
|
*/
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.listeners = new Map();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 14:52:50 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 节流函数
|
|
|
|
|
|
* 限制函数执行频率,使用 requestAnimationFrame 优化性能
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Function} func - 要节流的函数
|
|
|
|
|
|
* @param {number} wait - 等待时间(毫秒),默认 16ms(约 60fps)
|
|
|
|
|
|
* @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) {
|
|
|
|
|
|
// 否则使用 requestAnimationFrame 在下一帧执行
|
|
|
|
|
|
timeoutId = requestAnimationFrame(() => {
|
|
|
|
|
|
lastTime = Date.now();
|
|
|
|
|
|
timeoutId = null;
|
|
|
|
|
|
func.apply(this, args);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 防抖函数
|
|
|
|
|
|
* 延迟执行函数,直到事件停止触发指定时间后才执行
|
|
|
|
|
|
* 如果在等待期间再次触发,则重新计时
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Function} func - 要防抖的函数
|
|
|
|
|
|
* @param {number} wait - 等待时间(毫秒),默认 150ms
|
|
|
|
|
|
* @returns {Function} 防抖后的函数,包含 cancel 方法用于取消待执行任务
|
|
|
|
|
|
*/
|
|
|
|
|
|
debounce(func, wait = 150) {
|
|
|
|
|
|
let timeoutId = null;
|
|
|
|
|
|
|
|
|
|
|
|
const debounced = function(...args) {
|
|
|
|
|
|
// 取消之前的待执行任务
|
|
|
|
|
|
if (timeoutId) {
|
|
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置新的延迟执行任务
|
|
|
|
|
|
timeoutId = setTimeout(() => {
|
|
|
|
|
|
timeoutId = null;
|
|
|
|
|
|
func.apply(this, args);
|
|
|
|
|
|
}, wait);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 提供取消方法,允许手动取消待执行任务
|
|
|
|
|
|
debounced.cancel = function() {
|
|
|
|
|
|
if (timeoutId) {
|
|
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
|
|
timeoutId = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return debounced;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 14:36:49 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 添加事件监听器
|
|
|
|
|
|
* 自动跟踪监听器以便后续清理
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Element|Window|Document} element - DOM 元素、Window 或 Document 对象
|
|
|
|
|
|
* @param {string} event - 事件名称(如 'scroll', 'resize', 'click')
|
|
|
|
|
|
* @param {Function} handler - 事件处理函数
|
|
|
|
|
|
* @param {Object} options - 事件监听器选项(如 { passive: true })
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
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({ element, handler, options });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 移除事件监听器
|
|
|
|
|
|
* 移除指定元素上的指定事件的所有监听器
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Element|Window|Document} element - DOM 元素、Window 或 Document 对象
|
|
|
|
|
|
* @param {string} event - 事件名称
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除所有事件监听器
|
|
|
|
|
|
* 通常在 PJAX 页面切换或组件销毁时调用
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
clear() {
|
|
|
|
|
|
this.listeners.forEach((handlers, key) => {
|
|
|
|
|
|
// 从 key 中提取事件类型
|
|
|
|
|
|
const eventType = key.split('_').pop();
|
|
|
|
|
|
handlers.forEach(({ element, handler, options }) => {
|
|
|
|
|
|
if (element) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
element.removeEventListener(eventType, handler, options);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// 静默失败,元素可能已被移除
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
this.listeners.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成元素和事件的唯一键
|
|
|
|
|
|
*
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @param {Element|Window|Document} element - DOM 元素
|
|
|
|
|
|
* @param {string} event - 事件名称
|
|
|
|
|
|
* @returns {string} 唯一键
|
|
|
|
|
|
*/
|
|
|
|
|
|
_getKey(element, event) {
|
|
|
|
|
|
// 为元素生成唯一标识
|
|
|
|
|
|
if (!element._argonEventId) {
|
|
|
|
|
|
element._argonEventId = `elem_${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
return `${element._argonEventId}_${event}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 14:52:50 +08:00
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
// 资源加载模块
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 资源加载类
|
|
|
|
|
|
* 提供第三方库按需加载和缓存管理功能
|
|
|
|
|
|
*
|
|
|
|
|
|
* @class ArgonResourceLoader
|
|
|
|
|
|
*/
|
|
|
|
|
|
class ArgonResourceLoader {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构造函数
|
|
|
|
|
|
* 初始化加载状态管理
|
|
|
|
|
|
*/
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.loaded = new Set(); // 已加载的资源集合
|
|
|
|
|
|
this.loading = new Map(); // 正在加载的资源 Promise Map
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 按需加载脚本
|
|
|
|
|
|
* 如果资源已加载或正在加载,则复用缓存
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {string} name - 资源名称(如 'prism', 'zoomify', 'tippy')
|
|
|
|
|
|
* @param {string} url - 资源 URL
|
|
|
|
|
|
* @returns {Promise<void>} 加载完成的 Promise
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadScript(name, url) {
|
|
|
|
|
|
// 如果已加载,直接返回
|
|
|
|
|
|
if (this.loaded.has(name)) {
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果正在加载,返回现有的 Promise
|
|
|
|
|
|
if (this.loading.has(name)) {
|
|
|
|
|
|
return this.loading.get(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新的加载 Promise
|
|
|
|
|
|
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} from ${url}`));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
document.head.appendChild(script);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.loading.set(name, promise);
|
|
|
|
|
|
return promise;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查页面是否需要某个库
|
|
|
|
|
|
* 通过 CSS 选择器判断页面中是否存在需要该库的元素
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {string} selector - CSS 选择器
|
|
|
|
|
|
* @returns {boolean} 如果页面包含匹配的元素则返回 true
|
|
|
|
|
|
*/
|
|
|
|
|
|
needsLibrary(selector) {
|
|
|
|
|
|
return document.querySelector(selector) !== null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 条件加载 Prism 代码高亮库
|
|
|
|
|
|
* 仅在页面包含代码块时加载
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadPrismIfNeeded() {
|
|
|
|
|
|
// 检查是否需要加载
|
|
|
|
|
|
if (!this.needsLibrary('pre code')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已经存在
|
|
|
|
|
|
if (typeof window.Prism !== 'undefined' && window.Prism.highlightAll) {
|
|
|
|
|
|
this.loaded.add('prism');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载 Prism
|
|
|
|
|
|
if (typeof argonConfig !== 'undefined' && argonConfig.prism_url) {
|
|
|
|
|
|
await this.loadScript('prism', argonConfig.prism_url);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 条件加载 Zoomify 图片放大库
|
|
|
|
|
|
* 仅在页面包含图片时加载
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadZoomifyIfNeeded() {
|
|
|
|
|
|
// 检查是否需要加载
|
|
|
|
|
|
if (!this.needsLibrary('.post-content img')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已经存在
|
|
|
|
|
|
if (typeof window.Zoomify !== 'undefined') {
|
|
|
|
|
|
this.loaded.add('zoomify');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载 Zoomify
|
|
|
|
|
|
if (typeof argonConfig !== 'undefined' && argonConfig.zoomify_url) {
|
|
|
|
|
|
await this.loadScript('zoomify', argonConfig.zoomify_url);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 条件加载 Tippy 提示框库
|
|
|
|
|
|
* 仅在页面包含提示框元素时加载
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadTippyIfNeeded() {
|
|
|
|
|
|
// 检查是否需要加载
|
|
|
|
|
|
if (!this.needsLibrary('[data-tippy-content]')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已经存在
|
|
|
|
|
|
if (typeof window.tippy !== 'undefined') {
|
|
|
|
|
|
this.loaded.add('tippy');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载 Tippy
|
|
|
|
|
|
if (typeof argonConfig !== 'undefined' && argonConfig.tippy_url) {
|
|
|
|
|
|
await this.loadScript('tippy', argonConfig.tippy_url);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
// 渲染优化模块
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 渲染优化类
|
|
|
|
|
|
* 批量读写 DOM 避免布局抖动,管理 GPU 加速属性
|
|
|
|
|
|
*
|
|
|
|
|
|
* @class ArgonRenderOptimizer
|
|
|
|
|
|
*/
|
|
|
|
|
|
class ArgonRenderOptimizer {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构造函数
|
|
|
|
|
|
* 初始化读写队列和调度状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.readQueue = []; // DOM 读取操作队列
|
|
|
|
|
|
this.writeQueue = []; // DOM 写入操作队列
|
|
|
|
|
|
this.scheduled = false; // 是否已调度执行
|
2026-01-21 23:20:06 +08:00
|
|
|
|
this.activeAnimations = new Set(); // 当前活动的动画元素集合
|
|
|
|
|
|
this.maxAnimations = 3; // 最大同时运行的动画数量
|
|
|
|
|
|
this.animationQueue = []; // 等待执行的动画队列
|
2026-01-21 14:52:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量读取 DOM 属性
|
|
|
|
|
|
* 将读取操作加入队列,在下一帧统一执行
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Function} readFn - 读取函数,应该只包含 DOM 读取操作
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
read(readFn) {
|
|
|
|
|
|
this.readQueue.push(readFn);
|
|
|
|
|
|
this._schedule();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量写入 DOM
|
|
|
|
|
|
* 将写入操作加入队列,在所有读取操作完成后统一执行
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Function} writeFn - 写入函数,应该只包含 DOM 写入操作
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
write(writeFn) {
|
|
|
|
|
|
this.writeQueue.push(writeFn);
|
|
|
|
|
|
this._schedule();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 调度执行
|
|
|
|
|
|
* 使用 requestAnimationFrame 在下一帧执行队列中的操作
|
|
|
|
|
|
*
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
_schedule() {
|
|
|
|
|
|
if (this.scheduled) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.scheduled = true;
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
|
this._flush();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 执行队列
|
|
|
|
|
|
* 先执行所有读取操作,再执行所有写入操作
|
|
|
|
|
|
* 这样可以避免布局抖动(layout thrashing)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
_flush() {
|
|
|
|
|
|
// 先执行所有读取操作
|
|
|
|
|
|
while (this.readQueue.length) {
|
|
|
|
|
|
const readFn = this.readQueue.shift();
|
|
|
|
|
|
try {
|
|
|
|
|
|
readFn();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error in read operation:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 再执行所有写入操作
|
|
|
|
|
|
while (this.writeQueue.length) {
|
|
|
|
|
|
const writeFn = this.writeQueue.shift();
|
|
|
|
|
|
try {
|
|
|
|
|
|
writeFn();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error in write operation:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.scheduled = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 添加 GPU 加速提示
|
|
|
|
|
|
* 设置 will-change 属性,提示浏览器创建合成层
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Element} element - DOM 元素
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
enableGPU(element) {
|
|
|
|
|
|
if (!element) return;
|
|
|
|
|
|
element.style.willChange = 'transform, opacity';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 移除 GPU 加速提示
|
|
|
|
|
|
* 移除 will-change 属性,释放资源
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Element} element - DOM 元素
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
disableGPU(element) {
|
|
|
|
|
|
if (!element) return;
|
|
|
|
|
|
element.style.willChange = 'auto';
|
|
|
|
|
|
}
|
2026-01-21 23:20:06 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 开始动画
|
|
|
|
|
|
* 如果当前活动动画数量未达到上限,立即启动动画并启用 GPU 加速
|
|
|
|
|
|
* 否则将动画加入等待队列
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Element} element - 要动画的 DOM 元素
|
|
|
|
|
|
* @param {Function} animationFn - 动画函数,接收 element 作为参数
|
|
|
|
|
|
* @returns {boolean} 如果动画立即启动返回 true,否则返回 false
|
|
|
|
|
|
*/
|
|
|
|
|
|
startAnimation(element, animationFn) {
|
|
|
|
|
|
if (!element) return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果当前活动动画数量未达到上限,立即启动
|
|
|
|
|
|
if (this.activeAnimations.size < this.maxAnimations) {
|
|
|
|
|
|
this._executeAnimation(element, animationFn);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 否则加入等待队列
|
|
|
|
|
|
this.animationQueue.push({ element, animationFn });
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 结束动画
|
|
|
|
|
|
* 移除 GPU 加速提示,从活动动画集合中移除元素
|
|
|
|
|
|
* 如果有等待的动画,启动队列中的下一个动画
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Element} element - 动画完成的 DOM 元素
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
endAnimation(element) {
|
|
|
|
|
|
if (!element) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 禁用 GPU 加速
|
|
|
|
|
|
this.disableGPU(element);
|
|
|
|
|
|
|
|
|
|
|
|
// 从活动动画集合中移除
|
|
|
|
|
|
this.activeAnimations.delete(element);
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有等待的动画,启动下一个
|
|
|
|
|
|
if (this.animationQueue.length > 0) {
|
|
|
|
|
|
const { element: nextElement, animationFn } = this.animationQueue.shift();
|
|
|
|
|
|
this._executeAnimation(nextElement, animationFn);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 执行动画
|
|
|
|
|
|
* 启用 GPU 加速,将元素加入活动动画集合,执行动画函数
|
|
|
|
|
|
*
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @param {Element} element - DOM 元素
|
|
|
|
|
|
* @param {Function} animationFn - 动画函数
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
_executeAnimation(element, animationFn) {
|
|
|
|
|
|
// 启用 GPU 加速
|
|
|
|
|
|
this.enableGPU(element);
|
|
|
|
|
|
|
|
|
|
|
|
// 加入活动动画集合
|
|
|
|
|
|
this.activeAnimations.add(element);
|
|
|
|
|
|
|
|
|
|
|
|
// 执行动画函数
|
|
|
|
|
|
try {
|
|
|
|
|
|
animationFn(element);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Error executing animation:', error);
|
|
|
|
|
|
// 出错时也要清理
|
|
|
|
|
|
this.endAnimation(element);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前活动动画数量
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {number} 当前活动的动画数量
|
|
|
|
|
|
*/
|
|
|
|
|
|
getActiveAnimationCount() {
|
|
|
|
|
|
return this.activeAnimations.size;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取等待队列中的动画数量
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {number} 等待队列中的动画数量
|
|
|
|
|
|
*/
|
|
|
|
|
|
getQueuedAnimationCount() {
|
|
|
|
|
|
return this.animationQueue.length;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除所有动画
|
|
|
|
|
|
* 停止所有活动动画,清空等待队列
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
clearAllAnimations() {
|
|
|
|
|
|
// 禁用所有活动动画的 GPU 加速
|
|
|
|
|
|
this.activeAnimations.forEach(element => {
|
|
|
|
|
|
this.disableGPU(element);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 清空集合和队列
|
|
|
|
|
|
this.activeAnimations.clear();
|
|
|
|
|
|
this.animationQueue = [];
|
|
|
|
|
|
}
|
2026-01-21 14:52:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 23:29:55 +08:00
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
// 内存管理模块
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 内存管理类
|
|
|
|
|
|
* 跟踪和管理定时器、间隔定时器和动画帧,确保正确清理避免内存泄漏
|
|
|
|
|
|
*
|
|
|
|
|
|
* @class ArgonMemoryManager
|
|
|
|
|
|
*/
|
|
|
|
|
|
class ArgonMemoryManager {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构造函数
|
|
|
|
|
|
* 初始化 ID 跟踪集合
|
|
|
|
|
|
*/
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.timers = new Set(); // setTimeout ID 集合
|
|
|
|
|
|
this.intervals = new Set(); // setInterval ID 集合
|
|
|
|
|
|
this.frames = new Set(); // requestAnimationFrame ID 集合
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置定时器并跟踪
|
|
|
|
|
|
* 包装原生 setTimeout,自动跟踪 timer ID
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Function} callback - 回调函数
|
|
|
|
|
|
* @param {number} delay - 延迟时间(毫秒)
|
|
|
|
|
|
* @param {...*} args - 传递给回调函数的参数
|
|
|
|
|
|
* @returns {number} timer ID,可用于手动清除
|
|
|
|
|
|
*/
|
|
|
|
|
|
setTimeout(callback, delay, ...args) {
|
|
|
|
|
|
const id = setTimeout(() => {
|
|
|
|
|
|
// 执行完成后自动从跟踪集合中移除
|
|
|
|
|
|
this.timers.delete(id);
|
|
|
|
|
|
callback(...args);
|
|
|
|
|
|
}, delay);
|
|
|
|
|
|
|
|
|
|
|
|
this.timers.add(id);
|
|
|
|
|
|
return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置间隔定时器并跟踪
|
|
|
|
|
|
* 包装原生 setInterval,自动跟踪 interval ID
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Function} callback - 回调函数
|
|
|
|
|
|
* @param {number} interval - 间隔时间(毫秒)
|
|
|
|
|
|
* @param {...*} args - 传递给回调函数的参数
|
|
|
|
|
|
* @returns {number} interval ID,可用于手动清除
|
|
|
|
|
|
*/
|
|
|
|
|
|
setInterval(callback, interval, ...args) {
|
|
|
|
|
|
const id = setInterval(() => {
|
|
|
|
|
|
callback(...args);
|
|
|
|
|
|
}, interval);
|
|
|
|
|
|
|
|
|
|
|
|
this.intervals.add(id);
|
|
|
|
|
|
return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 请求动画帧并跟踪
|
|
|
|
|
|
* 包装原生 requestAnimationFrame,自动跟踪 frame ID
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Function} callback - 回调函数
|
|
|
|
|
|
* @returns {number} frame ID,可用于手动取消
|
|
|
|
|
|
*/
|
|
|
|
|
|
requestAnimationFrame(callback) {
|
|
|
|
|
|
const id = requestAnimationFrame((timestamp) => {
|
|
|
|
|
|
// 执行完成后自动从跟踪集合中移除
|
|
|
|
|
|
this.frames.delete(id);
|
|
|
|
|
|
callback(timestamp);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.frames.add(id);
|
|
|
|
|
|
return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除定时器
|
|
|
|
|
|
* 包装原生 clearTimeout,同时从跟踪集合中移除
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {number} id - timer ID
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
clearTimeout(id) {
|
|
|
|
|
|
clearTimeout(id);
|
|
|
|
|
|
this.timers.delete(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除间隔定时器
|
|
|
|
|
|
* 包装原生 clearInterval,同时从跟踪集合中移除
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {number} id - interval ID
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
clearInterval(id) {
|
|
|
|
|
|
clearInterval(id);
|
|
|
|
|
|
this.intervals.delete(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 取消动画帧
|
|
|
|
|
|
* 包装原生 cancelAnimationFrame,同时从跟踪集合中移除
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {number} id - frame ID
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
cancelAnimationFrame(id) {
|
|
|
|
|
|
cancelAnimationFrame(id);
|
|
|
|
|
|
this.frames.delete(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清理所有定时器和动画帧
|
|
|
|
|
|
* 通常在 PJAX 页面切换或组件销毁时调用
|
|
|
|
|
|
* 确保所有待执行的回调都被取消,避免内存泄漏
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {void}
|
|
|
|
|
|
*/
|
|
|
|
|
|
clearAll() {
|
|
|
|
|
|
// 清除所有 setTimeout
|
|
|
|
|
|
this.timers.forEach(id => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
clearTimeout(id);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// 静默失败,ID 可能已失效
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 清除所有 setInterval
|
|
|
|
|
|
this.intervals.forEach(id => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
clearInterval(id);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// 静默失败,ID 可能已失效
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 取消所有 requestAnimationFrame
|
|
|
|
|
|
this.frames.forEach(id => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
cancelAnimationFrame(id);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// 静默失败,ID 可能已失效
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 清空所有集合
|
|
|
|
|
|
this.timers.clear();
|
|
|
|
|
|
this.intervals.clear();
|
|
|
|
|
|
this.frames.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前跟踪的定时器数量
|
|
|
|
|
|
* 用于调试和性能监控
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {Object} 包含各类定时器数量的对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
getStats() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
timers: this.timers.size,
|
|
|
|
|
|
intervals: this.intervals.size,
|
|
|
|
|
|
frames: this.frames.size,
|
|
|
|
|
|
total: this.timers.size + this.intervals.size + this.frames.size
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 14:36:49 +08:00
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
// 模块导出和初始化接口
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 性能优化模块初始化函数
|
|
|
|
|
|
* 在主题加载时调用,初始化所有优化模块
|
|
|
|
|
|
*/
|
|
|
|
|
|
function initArgonPerformance() {
|
|
|
|
|
|
// 根据调试模式设置性能监控
|
|
|
|
|
|
if (typeof argonConfig !== 'undefined' && argonConfig.debug_mode) {
|
|
|
|
|
|
ArgonPerformanceConfig.monitor.enabled = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化各个模块的代码将在后续任务中添加
|
|
|
|
|
|
console.info('Argon Performance Optimization Module Loaded');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 导出配置对象和类供其他模块使用
|
|
|
|
|
|
if (typeof window !== 'undefined') {
|
|
|
|
|
|
window.ArgonPerformanceConfig = ArgonPerformanceConfig;
|
|
|
|
|
|
window.ArgonDOMCache = ArgonDOMCache;
|
|
|
|
|
|
window.ArgonEventManager = ArgonEventManager;
|
2026-01-21 14:52:50 +08:00
|
|
|
|
window.ArgonResourceLoader = ArgonResourceLoader;
|
|
|
|
|
|
window.ArgonRenderOptimizer = ArgonRenderOptimizer;
|
2026-01-21 23:29:55 +08:00
|
|
|
|
window.ArgonMemoryManager = ArgonMemoryManager;
|
2026-01-21 14:36:49 +08:00
|
|
|
|
window.initArgonPerformance = initArgonPerformance;
|
|
|
|
|
|
}
|