Files
argon-theme/RENDER_OPTIMIZER_USAGE.md
nanhaoluo 9fca9481ae feat: 实现 ArgonRenderOptimizer 类
- 实现构造函数,初始化读写队列和调度状态
- 实现 read() 方法:将 DOM 读取操作加入队列
- 实现 write() 方法:将 DOM 写入操作加入队列
- 实现 _schedule() 私有方法:使用 requestAnimationFrame 调度执行
- 实现 _flush() 私有方法:批量执行队列操作(先读后写)
- 实现 enableGPU() 方法:设置 will-change 属性启用 GPU 加速
- 实现 disableGPU() 方法:移除 will-change 属性释放资源
- 添加错误处理机制,确保单个操作失败不影响其他操作
- 在测试文件中添加完整的渲染优化模块测试
- 创建使用指南文档 RENDER_OPTIMIZER_USAGE.md
- 导出 ArgonRenderOptimizer 类供其他模块使用

验证需求:2.3, 2.4, 17.1, 17.2
2026-01-21 14:52:50 +08:00

296 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ArgonRenderOptimizer 使用指南
## 概述
`ArgonRenderOptimizer` 类用于优化 DOM 操作性能通过批量读写避免布局抖动Layout Thrashing并提供 GPU 加速管理功能。
## 核心功能
### 1. 批量读写 DOM
**问题:** 频繁交替读写 DOM 会导致浏览器多次重排reflow严重影响性能。
**解决方案:** 使用 `read()``write()` 方法将操作加入队列,在下一帧统一执行(先读后写)。
### 2. GPU 加速管理
**功能:** 通过 `will-change` 属性提示浏览器创建合成层,利用 GPU 加速动画。
## 使用示例
### 基础用法
```javascript
// 创建实例
const optimizer = new ArgonRenderOptimizer();
// 批量读取 DOM 属性
let scrollTop, windowHeight;
optimizer.read(() => {
scrollTop = document.documentElement.scrollTop;
windowHeight = window.innerHeight;
});
// 批量写入 DOM
optimizer.write(() => {
const toolbar = document.querySelector('.navbar');
if (toolbar) {
toolbar.style.opacity = scrollTop > 100 ? '1' : '0.8';
}
});
```
### 滚动事件优化
```javascript
// 错误示例:频繁交替读写导致布局抖动
window.addEventListener('scroll', () => {
const scrollTop = document.documentElement.scrollTop; // 读
toolbar.style.opacity = scrollTop > 100 ? '1' : '0.8'; // 写
const windowHeight = window.innerHeight; // 读
sidebar.style.height = windowHeight + 'px'; // 写
// 每次滚动都会触发多次重排!
});
// 正确示例:使用 ArgonRenderOptimizer
window.addEventListener('scroll', () => {
let scrollTop, windowHeight;
// 批量读取
optimizer.read(() => {
scrollTop = document.documentElement.scrollTop;
windowHeight = window.innerHeight;
});
// 批量写入
optimizer.write(() => {
toolbar.style.opacity = scrollTop > 100 ? '1' : '0.8';
sidebar.style.height = windowHeight + 'px';
});
// 只会触发一次重排!
});
```
### GPU 加速动画
```javascript
const element = document.querySelector('.animated-element');
// 动画开始前启用 GPU 加速
optimizer.enableGPU(element);
// 执行动画
element.style.transform = 'translateX(100px)';
// 动画结束后禁用 GPU 加速(释放资源)
element.addEventListener('transitionend', () => {
optimizer.disableGPU(element);
}, { once: true });
```
### 复杂场景示例
```javascript
// 瀑布流布局重新计算
function recalculateWaterfallLayout() {
const items = document.querySelectorAll('.waterfall-item');
const itemHeights = [];
const columnHeights = [0, 0, 0]; // 3 列
// 第一步:批量读取所有元素高度
optimizer.read(() => {
items.forEach(item => {
itemHeights.push(item.offsetHeight);
});
});
// 第二步:批量写入所有元素位置
optimizer.write(() => {
items.forEach((item, index) => {
// 找到最短的列
const minColumn = columnHeights.indexOf(Math.min(...columnHeights));
// 设置元素位置
item.style.position = 'absolute';
item.style.left = (minColumn * 33.33) + '%';
item.style.top = columnHeights[minColumn] + 'px';
// 更新列高度
columnHeights[minColumn] += itemHeights[index];
});
});
}
```
## API 参考
### `read(readFn)`
将 DOM 读取操作加入队列。
**参数:**
- `readFn` (Function): 读取函数,应该只包含 DOM 读取操作(如 `offsetHeight``scrollTop` 等)
**返回值:** `void`
### `write(writeFn)`
将 DOM 写入操作加入队列。
**参数:**
- `writeFn` (Function): 写入函数,应该只包含 DOM 写入操作(如修改 `style``className` 等)
**返回值:** `void`
### `enableGPU(element)`
为元素启用 GPU 加速提示。
**参数:**
- `element` (Element): 需要加速的 DOM 元素
**返回值:** `void`
**注意:** 不要滥用此功能,过多的合成层会占用大量内存。
### `disableGPU(element)`
移除元素的 GPU 加速提示。
**参数:**
- `element` (Element): DOM 元素
**返回值:** `void`
## 性能优势
### 布局抖动消除
**优化前:**
```javascript
// 交替读写,触发 4 次重排
const h1 = elem1.offsetHeight; // 读 → 重排
elem1.style.height = h1 + 10 + 'px'; // 写
const h2 = elem2.offsetHeight; // 读 → 重排
elem2.style.height = h2 + 10 + 'px'; // 写
const h3 = elem3.offsetHeight; // 读 → 重排
elem3.style.height = h3 + 10 + 'px'; // 写
const h4 = elem4.offsetHeight; // 读 → 重排
elem4.style.height = h4 + 10 + 'px'; // 写
```
**优化后:**
```javascript
// 批量读写,只触发 1 次重排
let h1, h2, h3, h4;
optimizer.read(() => {
h1 = elem1.offsetHeight;
h2 = elem2.offsetHeight;
h3 = elem3.offsetHeight;
h4 = elem4.offsetHeight;
});
optimizer.write(() => {
elem1.style.height = h1 + 10 + 'px';
elem2.style.height = h2 + 10 + 'px';
elem3.style.height = h3 + 10 + 'px';
elem4.style.height = h4 + 10 + 'px';
});
```
**性能提升:** 减少 75% 的重排次数!
## 最佳实践
1. **分离读写操作**:始终将读取和写入操作分开
2. **避免在循环中使用**:不要在循环内部调用 `read()``write()`
3. **合理使用 GPU 加速**:只在动画元素上使用,动画结束后及时移除
4. **结合事件节流**:在高频事件(如 scroll、resize中配合使用节流机制
## 测试
运行测试文件验证功能:
```bash
# 在浏览器中打开
argon-performance.test.html
```
测试包括:
- ✓ 读写队列正常工作
- ✓ 批量操作顺序正确(先读后写)
- ✓ GPU 加速属性设置和移除
- ✓ 错误处理机制
## 相关需求
- **需求 2.3**: 批量读取 DOM 属性,避免布局抖动
- **需求 2.4**: 批量写入 DOM在读取完成后统一执行
- **需求 17.1**: 批量读取所有需要的属性
- **需求 17.2**: 在读取完成后批量写入
## 技术原理
### 布局抖动Layout Thrashing
当 JavaScript 交替读写 DOM 时,浏览器会被迫多次计算布局:
1. 读取属性(如 `offsetHeight`)→ 浏览器计算布局
2. 修改样式 → 标记布局为脏
3. 再次读取属性 → 浏览器重新计算布局
4. 再次修改样式 → 再次标记为脏
5. ...循环往复
### 批量处理原理
`ArgonRenderOptimizer` 使用 `requestAnimationFrame` 将所有操作延迟到下一帧:
1. 收集所有读取操作到 `readQueue`
2. 收集所有写入操作到 `writeQueue`
3. 在下一帧统一执行:
- 先执行所有读取操作
- 再执行所有写入操作
4. 浏览器只需计算一次布局
### GPU 加速原理
`will-change` 属性提示浏览器:
1. 为元素创建独立的合成层Composite Layer
2. 将该层的渲染交给 GPU 处理
3. 动画时只需更新该层,不影响其他元素
4. 大幅提升动画性能60fps
## 注意事项
⚠️ **不要滥用 GPU 加速**
每个合成层都会占用内存,过多的层会导致:
- 内存占用过高
- 反而降低性能
- 移动设备可能崩溃
建议:
- 只在动画元素上使用
- 动画结束后立即移除
- 同时运行的动画不超过 3 个
⚠️ **错误处理**
`_flush()` 方法会捕获并记录错误,但不会中断其他操作的执行。
## 更新日志
### 2026-01-16
- ✅ 实现 ArgonRenderOptimizer 类
- ✅ 实现读写队列和批量处理
- ✅ 实现 GPU 加速管理
- ✅ 添加完整的测试用例
- ✅ 添加错误处理机制