Skip to content

Commit 39c4614

Browse files
committed
perf: P1 性能优化(后处理分档 + 定时器清理 + 细粒度订阅)
1) 从容(medium)档降级后处理:浅色关色差(ChromaticAberration)、深色关景深(DepthOfField,最重 pass),澎湃档全开——降低默认档 GPU 开销;色差 offset 改模块级常量避免每帧 new Vector2;2) useCardInteraction/useSocraticFlow 的 setTimeout 统一 ref + 卸载清理,避免卸载后 setState;3) useSocraticFlow 无 selector 整 store 订阅改细粒度 action 订阅。
1 parent fb33cbe commit 39c4614

4 files changed

Lines changed: 58 additions & 11 deletions

File tree

‎client/src/features/feynman/hooks/useSocraticFlow.tsx‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @ai-context: feynman 功能模块 Hook:useSocraticFlow。
66
*/
77

8-
import { useState, useCallback, useRef } from 'react';
8+
import { useState, useCallback, useRef, useEffect } from 'react';
99
import { useNavigate } from 'react-router-dom';
1010
import { useToast } from '@/components/ui';
1111
import { useAISocratic } from '@/lib/ai/hooks/useAISocratic';
@@ -68,7 +68,10 @@ function mapToDeepeningAngles(aiAngles: Array<{ key: string; label: string; ques
6868
export function useSocraticFlow() {
6969
const navigate = useNavigate();
7070
const { toast } = useToast();
71-
const { createNote, updateNote } = useFeynmanStore();
71+
// 细粒度订阅 action(恒定引用),避免 useFeynmanStore() 无 selector 订阅整个 store
72+
// 导致笔记列表/薄弱点等任意变化都重渲染苏格拉底会话页(P1-5 性能修复)
73+
const createNote = useFeynmanStore((s) => s.createNote);
74+
const updateNote = useFeynmanStore((s) => s.updateNote);
7275
const { brainstorm: brainstormAI, question: questionAI, evaluate: evaluateAI, deepening: deepeningAI } = useAISocratic();
7376

7477
// ── State ──
@@ -85,11 +88,19 @@ export function useSocraticFlow() {
8588
const [deepeningFallbackMsg, setDeepeningFallbackMsg] = useState<string | null>(null);
8689

8790
const conversationIdRef = useRef(`socratic_${Date.now()}`);
91+
// 阶段过渡定时器 ref:卸载时清理,避免卸载后 setState/navigate(P1-9 性能修复)
92+
const phaseTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
93+
94+
useEffect(() => {
95+
return () => {
96+
if (phaseTimerRef.current) clearTimeout(phaseTimerRef.current);
97+
};
98+
}, []);
8899

89100
// ── 阶段过渡辅助 ──
90101
const transitionToPhase = useCallback((nextPhase: Phase, onEnter?: () => void | Promise<void>) => {
91102
setExiting(true);
92-
setTimeout(async () => {
103+
phaseTimerRef.current = setTimeout(async () => {
93104
setPhase(nextPhase);
94105
setExiting(false);
95106
if (onEnter) await onEnter();
@@ -254,7 +265,7 @@ export function useSocraticFlow() {
254265
toast({ type: 'success', message: '已保存为浮出水面概念!' });
255266

256267
setExiting(true);
257-
setTimeout(() => { navigate(`/feynman/${noteId}`); }, PHASE_TRANSITION_MS);
268+
phaseTimerRef.current = setTimeout(() => { navigate(`/feynman/${noteId}`); }, PHASE_TRANSITION_MS);
258269
} catch {
259270
toast({ type: 'error', message: '保存失败,请稍后重试' });
260271
setSavingNote(false);

‎client/src/features/flashcards/hooks/useCardInteraction.ts‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export function useCardInteraction({
4343
const [confidence, setConfidence] = useState<Confidence | null>(null);
4444
const prevIndexRef = useRef(currentIndex);
4545
const exitingRef = useRef(false);
46+
// 定时器 ref:卸载时清理,避免卸载后 setState(P1-9 性能修复)
47+
const plusOneTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
48+
const exitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
4649

4750
// 拖拽手势
4851
const dragX = useMotionValue(0);
@@ -74,20 +77,28 @@ export function useCardInteraction({
7477
exitingRef.current = exiting;
7578
}, [exiting]);
7679

80+
// 卸载时清理定时器,避免卸载后 setState / 会话结束后误触发 rateCard
81+
useEffect(() => {
82+
return () => {
83+
if (plusOneTimerRef.current) clearTimeout(plusOneTimerRef.current);
84+
if (exitTimerRef.current) clearTimeout(exitTimerRef.current);
85+
};
86+
}, []);
87+
7788
const handleRate = useCallback((rating: Rating) => {
7889
if (exitingRef.current) return;
7990
// v0.9.0: default to 'medium' if no confidence selected
8091
const effectiveConfidence = confidence ?? 'medium';
8192
if (current && current.repetitions === 0 && rating !== Rating.Again) {
8293
setSessionMastered((n) => n + 1);
8394
setShowPlusOne(true);
84-
setTimeout(() => setShowPlusOne(false), PLUS_ONE_ANIMATION_DURATION_MS);
95+
plusOneTimerRef.current = setTimeout(() => setShowPlusOne(false), PLUS_ONE_ANIMATION_DURATION_MS);
8596
}
8697
setCardGlow(rating === Rating.Again ? 'wrong' : 'correct');
8798
setExitDir(rating === Rating.Again ? 'left' : 'right');
8899
setExiting(true);
89100
exitingRef.current = true;
90-
setTimeout(() => {
101+
exitTimerRef.current = setTimeout(() => {
91102
setExiting(false);
92103
exitingRef.current = false;
93104
setFlipDone(false);

‎client/src/lib/3d/scenes/AuroraDomeWorld.tsx‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ function CloudLayer() {
268268
// 行星,外层行星只改状态不跳转,导致“点击 3D 物体”与功能错位。
269269

270270
// ─── 主场景组件 ───────────────────────────────────────────
271+
/** 色差偏移量(模块级常量,避免每帧 new Vector2 的 GC 压力) */
272+
const CHROMATIC_OFFSET = new THREE.Vector2(0.001, 0.001);
273+
271274
export function AuroraDomeWorld() {
272275
// 有效 tier(自动 tier 受用户性能模式上限约束)
273276
const tier = useEffectiveTier();
@@ -298,8 +301,9 @@ export function AuroraDomeWorld() {
298301
{/* 云层效果(低性能时隐藏) */}
299302
{tier !== 'low' && <CloudLayer />}
300303

301-
{/* 后处理(低性能时关闭) */}
302-
{tier !== 'low' && (
304+
{/* 后处理:低档全关;中档关色差以降 GPU(色差是较贵的全屏 pass);澎湃档全开。
305+
条件置于 composer 层级(group 接受 false),避免 EffectComposer 子元素严格类型报错 */}
306+
{tier === 'low' ? null : tier === 'high' ? (
303307
<SafeEffectComposer>
304308
<Bloom
305309
intensity={0.3}
@@ -309,12 +313,22 @@ export function AuroraDomeWorld() {
309313
/>
310314
<ChromaticAberration
311315
blendFunction={BlendFunction.NORMAL}
312-
offset={new THREE.Vector2(0.001, 0.001)}
316+
offset={CHROMATIC_OFFSET}
313317
radialModulation={false}
314318
modulationOffset={0}
315319
/>
316320
<Vignette offset={0.4} darkness={0.3} />
317321
</SafeEffectComposer>
322+
) : (
323+
<SafeEffectComposer>
324+
<Bloom
325+
intensity={0.3}
326+
luminanceThreshold={0.8}
327+
luminanceSmoothing={0.3}
328+
mipmapBlur
329+
/>
330+
<Vignette offset={0.4} darkness={0.3} />
331+
</SafeEffectComposer>
318332
)}
319333
</group>
320334
);

‎client/src/lib/3d/scenes/DeepSeaWorld.tsx‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ export function DeepSeaWorld() {
2222
<meshBasicMaterial color="#0A1628" side={2} />
2323
</mesh>
2424

25-
{/* 后处理(低性能时关闭) */}
26-
{tier !== 'low' && (
25+
{/* 后处理:低档全关;中档关景深(DepthOfField 是最重的后处理 pass);澎湃档全开。
26+
条件置于 composer 层级(group 接受 false),避免 EffectComposer 子元素严格类型报错 */}
27+
{tier === 'low' ? null : tier === 'high' ? (
2728
<SafeEffectComposer>
2829
<Bloom
2930
intensity={0.5}
@@ -38,6 +39,16 @@ export function DeepSeaWorld() {
3839
/>
3940
<Vignette offset={0.3} darkness={0.7} />
4041
</SafeEffectComposer>
42+
) : (
43+
<SafeEffectComposer>
44+
<Bloom
45+
intensity={0.5}
46+
luminanceThreshold={0.6}
47+
luminanceSmoothing={0.9}
48+
mipmapBlur
49+
/>
50+
<Vignette offset={0.3} darkness={0.7} />
51+
</SafeEffectComposer>
4152
)}
4253
</group>
4354
);

0 commit comments

Comments
 (0)