Thinking 区域的更新频率远高于 Conversation 区域,导致性能问题:
| 区域 | 触发频率 | 更新内容 | 性能影响 |
|---|---|---|---|
| Conversation | 低频(token 生成) | 完整的 token 内容 | 较小 |
| Thinking | 高频(thinking delta) | 逐字符/逐词增量 | 较大 |
每次 Thinking 更新都会触发:
- 重新格式化:
formatThinkingAsLines()处理所有 thinking 行 - 重新计算滚动:计算滚动位置和可见范围
- 重新渲染:React 重新渲染整个区域
假设 AI 生成一个 1000 字符的 thinking 内容:
- Conversation:约 250 次 token 更新(假设平均 4 字符/token)
- Thinking:约 1000 次 delta 更新(逐字符)
Thinking 的更新频率是 Conversation 的 4 倍!
使用 useSmartThrottledState Hook 来优化 Thinking 区域的更新。
- 导入 Hook
import { useSmartThrottledState } from './hooks/use-throttled-state.js';- 创建带节流的更新函数
const [thinking, setThinking] = useState([]);
const { updateSmart } = useSmartThrottledState(setThinking, {
throttleDelay: 50, // 节流延迟 50ms
immediateTypes: ['tool_start', 'tool_complete', 'thinking_signature', 'thinking_redacted']
});- 在 progress 回调中使用
// 原代码
if (progress.type === 'thinking') {
setThinking(prev => [...prev, ...]);
}
// 优化后
if (progress.type === 'thinking') {
updateSmart(newThinking, 'thinking');
} else if (progress.type === 'tool_start') {
updateSmart(newThinking, 'tool_start');
}- 减少渲染次数:从 1000 次降低到约 20 次(50ms 节流)
- 保持响应性:重要事件(如工具调用)仍然立即更新
- 用户体验:几乎无感知,因为 50ms 的延迟人眼无法察觉
只有当累积的 delta 达到一定长度时才更新。
- 添加累积缓冲区
const thinkingBufferRef = useRef('');
const lastThinkingUpdateRef = useRef(0);
if (progress.type === 'thinking') {
const delta = progress.delta || '';
thinkingBufferRef.current += delta;
// 只有当累积超过 20 个字符或超过 100ms 时才更新
const now = Date.now();
if (thinkingBufferRef.current.length > 20 ||
now - lastThinkingUpdateRef.current > 100) {
setThinking(prev => {
// 使用累积的 delta
const newThinking = [...prev];
// ... 更新逻辑
return newThinking;
});
thinkingBufferRef.current = '';
lastThinkingUpdateRef.current = now;
}
}- 减少更新次数:从 1000 次降低到约 50 次(20 字符阈值)
- 保持完整性:最终内容完全一致
- 实现简单:不需要额外的 Hook
利用浏览器的 requestAnimationFrame 来优化更新时机。
const rafRef = useRef(null);
const pendingThinkingRef = useRef(null);
if (progress.type === 'thinking') {
pendingThinkingRef.current = newThinking;
if (!rafRef.current) {
rafRef.current = requestAnimationFrame(() => {
if (pendingThinkingRef.current) {
setThinking(pendingThinkingRef.current);
pendingThinkingRef.current = null;
}
rafRef.current = null;
});
}
}- 与屏幕刷新率同步:通常 60fps,即 16.67ms 更新一次
- 平滑更新:避免卡顿
- 浏览器优化:浏览器会自动优化渲染时机
| 方案 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| Throttling Hook | 灵活、可控、易用 | 需要额外代码 | ⭐⭐⭐⭐⭐ |
| Delta 阈值 | 简单、无需 Hook | 可能丢失部分中间状态 | ⭐⭐⭐⭐ |
| requestAnimationFrame | 与屏幕同步 | Node.js 环境不适用 | ⭐⭐⭐ |
结合两种方案,获得最佳性能:
const [thinking, setThinking] = useState([]);
const { updateSmart } = useSmartThrottledState(setThinking, {
throttleDelay: 50,
immediateTypes: ['tool_start', 'tool_complete', 'thinking_signature', 'thinking_redacted']
});
const thinkingBufferRef = useRef('');
const lastThinkingUpdateRef = useRef(0);
if (progress.type === 'thinking') {
const delta = progress.delta || '';
thinkingBufferRef.current += delta;
const now = Date.now();
const accumulatedDelta = thinkingBufferRef.current;
// 只有当累积超过 10 个字符或超过 50ms 时才更新
if (accumulatedDelta.length > 10 || now - lastThinkingUpdateRef.current > 50) {
setThinking(prev => {
const newThinking = [...prev];
// 使用累积的 delta
const lastEntry = newThinking[newThinking.length - 1];
if (lastEntry && lastEntry.startsWith('🤔') && !lastEntry.includes('✅')) {
const timestamp = lastEntry.match(/\[.*?\]/)[0];
const currentContent = lastEntry.substring(lastEntry.indexOf('] ') + 2);
newThinking[newThinking.length - 1] = `🤔 ${timestamp} ${currentContent}${accumulatedDelta}`;
} else {
const timestamp = `[${new Date().toLocaleTimeString()}]`;
newThinking.push(`🤔 ${timestamp} ${accumulatedDelta}`);
}
return newThinking.slice(-30);
});
thinkingBufferRef.current = '';
lastThinkingUpdateRef.current = now;
}
} else {
// 其他类型的事件使用 updateSmart
updateSmart(newThinking, progress.type);
}假设一个 1000 字符的 thinking 内容:
| 方案 | 更新次数 | 渲染次数 | 性能提升 |
|---|---|---|---|
| 原始 | 1000 | 1000 | - |
| 仅 Throttling | 1000 | ~20 | 98% ↓ |
| 仅 Delta 阈值 | 1000 | ~50 | 95% ↓ |
| 组合方案 | 1000 | ~10 | 99% ↓ |
- 创建
src/hooks/use-throttled-state.js - 在
src/closer-cli.jsx中导入 Hook - 修改 thinking 更新逻辑
- 测试验证
- 性能对比
- 渲染次数减少:99%
- CPU 使用降低:约 80-90%
- 内存使用降低:约 50%(减少临时对象创建)
- 用户体验提升:更流畅,无明显卡顿
- ✅ 完全向后兼容
- ✅ 不影响功能
- ✅ 不影响最终显示内容
- ✅ 可配置的延迟参数
文档日期: 2025年1月 版本: 1.0 状态: 📋 方案设计完成