Skip to content

Commit 98dc76e

Browse files
committed
refactor(ui): 优化 ThemeableStatic 组件及 ThemeManager 初始化逻辑
- 用 ink 的 Static 组件替代 Box,避免重复渲染已渲染项 - 通过 React.memo 和自定义比较函数跳过 render prop.children 比较 - ThemeableStatic 使用复合 key 强制重挂载以响应主题或重置变动 - ThemeManager init 方法仅在主题实际变化时通知监听器,避免重复渲染 - 精简主题应用逻辑,提升启动性能和渲染效率
1 parent e4c3584 commit 98dc76e

2 files changed

Lines changed: 29 additions & 10 deletions

File tree

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useMemo } from "react";
2-
import { Box } from "ink";
2+
import { Static } from "ink";
33

44
type Props<T> = {
55
items: T[];
@@ -14,22 +14,28 @@ type Props<T> = {
1414
*
1515
* Ink 的 <Static> 组件只渲染新增的 items,已渲染的 items 不会重新渲染。
1616
* 这个组件始终渲染所有 items,使用 key={themeVersion}:{resetKey} 在主题变化或内容重置时强制重新挂载。
17+
*
18+
* 使用 React.memo + 自定义比较跳过 children (render prop) 的比较,
19+
* 避免父组件因 nowTick 等无关状态变化导致每帧重渲染。
1720
*/
18-
export default function ThemeableStatic<T>({
21+
const ThemeableStaticInner = function ThemeableStaticInner<T>({
1922
items,
2023
themeVersion,
2124
resetKey,
2225
children: render,
2326
}: Props<T>): React.ReactElement {
24-
const children = useMemo(() => {
25-
return items.map((item, index) => render(item, index));
26-
}, [items, render]);
27-
2827
const compositeKey = `${themeVersion}:${resetKey ?? 0}`;
2928

29+
const wrappedRender = useMemo(() => (item: T, index: number) => render(item, index), [render]);
3030
return (
31-
<Box key={compositeKey} flexDirection="column">
32-
{children}
33-
</Box>
31+
<Static key={compositeKey} items={items}>
32+
{wrappedRender}
33+
</Static>
3434
);
35+
};
36+
37+
function propsAreEqual(prev: Props<unknown>, next: Props<unknown>): boolean {
38+
return prev.items === next.items && prev.themeVersion === next.themeVersion && prev.resetKey === next.resetKey;
3539
}
40+
41+
export default React.memo(ThemeableStaticInner, propsAreEqual) as typeof ThemeableStaticInner;

src/ui/theme/ThemeManager.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,23 @@ export class ThemeManager {
3131
/**
3232
* 异步初始化(含 OSC 11 终端背景查询)。
3333
* 应在 App 启动时调用一次。
34+
*
35+
* 只有主题实际变化时才通知监听器,避免首次挂载时因 themeVersion
36+
* 递增导致 Static 组件卸载再挂载,产生重复的欢迎页渲染。
3437
*/
3538
async init(): Promise<void> {
3639
this.terminalBg = await detectTerminalThemeAsync();
37-
this.refreshFromSettings();
40+
const themeSettings = this.loadThemeSettings();
41+
const newTheme = this.resolveWithContrast(themeSettings);
42+
const newPreset = this.loadPresetFromSettings();
43+
44+
if (newPreset !== this.currentPreset) {
45+
this.applyTheme(newTheme, newPreset);
46+
} else {
47+
this.currentTheme = newTheme;
48+
this.currentPreset = newPreset;
49+
setCurrentTheme(newTheme);
50+
}
3851
}
3952

4053
/**

0 commit comments

Comments
 (0)