You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复React组件频繁重渲染?Next.js转录同步场景优化

解决方案:优化Next.js转录页面的重渲染性能

针对你遇到的currentWord变化导致所有TranscriptText组件重渲染的性能问题,以下是几种可落地的修复和重构方案,比单纯使用useMemo/useCallback更有效:

1. 用React.memo+精准props传递减少不必要重渲染

默认情况下父组件状态变化会触发所有子组件重渲染,你可以给TranscriptText加上React.memo,同时只传递该组件是否需要高亮的布尔值,而非全局的currentWord对象:

// TranscriptText.js
const TranscriptText = React.memo(({ words, isHighlighted, onClick }) => {
  // 组件原有逻辑
});

// 父组件渲染逻辑
{speakerGroups.map(group => {
  // 提前判断当前组是否包含currentWord,仅传递布尔值
  const isGroupHighlighted = group.words.some(word => word.id === currentWord.id);
  return (
    <div key={group.speakerId}>
      <SpeakerName name={group.speakerName} />
      <TranscriptText 
        words={group.words} 
        isHighlighted={isGroupHighlighted}
        onClick={handleWordClick}
      />
    </div>
  );
})}

这样只有包含currentWord的组对应的TranscriptText才会重渲染,其他组因props无变化会被React.memo跳过。

2. 虚拟滚动:只渲染可见区域的组件

如果说话人组数量达100+,即使优化重渲染,过多DOM节点仍会影响性能。可使用虚拟滚动库(如react-window)仅渲染当前视口内的组:

import { FixedSizeList } from 'react-window';

// 父组件中替换原map渲染逻辑
<FixedSizeList
  height={600}
  itemCount={speakerGroups.length}
  itemSize={80} // 每个组的预估高度
>
  {({ index, style }) => {
    const group = speakerGroups[index];
    const isGroupHighlighted = group.words.some(word => word.id === currentWord.id);
    return (
      <div style={style} key={group.speakerId}>
        <SpeakerName name={group.speakerName} />
        <TranscriptText 
          words={group.words} 
          isHighlighted={isGroupHighlighted}
          onClick={handleWordClick}
        />
      </div>
    );
  }}
</FixedSizeList>

虚拟滚动会大幅减少同时存在的DOM节点数量,即使currentWord变化,也只有视口内的组件会重渲染。

3. 优化currentWord的更新频率

timeupdate事件每秒触发多次,频繁更新currentWord会导致大量重渲染。可给更新逻辑加防抖,或仅在单词切换时更新状态:

useEffect(() => {
  let lastWordId = null;
  const handleTimeUpdate = () => {
    const currentTime = mediaRef.current.currentTime;
    // 匹配当前时间对应的单词
    const word = transcript.words.find(w => w.start <= currentTime && w.end >= currentTime);
    if (word && word.id !== lastWordId) {
      setCurrentWord(word);
      lastWordId = word.id;
    }
  };

  mediaRef.current?.addEventListener('timeupdate', handleTimeUpdate);
  return () => mediaRef.current?.removeEventListener('timeupdate', handleTimeUpdate);
}, [transcript.words]);

这样只有当currentWord真正切换时才会更新状态,减少不必要的setState触发。

4. 用useRef替代state存储currentWord(进阶方案)

如果不需要currentWord触发组件重渲染,仅用于判断高亮,可用useRef存储currentWord,直接操作DOM实现高亮:

const currentWordRef = useRef(null);

useEffect(() => {
  const handleTimeUpdate = () => {
    const currentTime = mediaRef.current.currentTime;
    const word = transcript.words.find(w => w.start <= currentTime && w.end >= currentTime);
    if (word && word.id !== currentWordRef.current?.id) {
      // 移除之前高亮的元素
      currentWordRef.current?.element?.classList.remove('highlighted');
      // 高亮当前单词对应的DOM元素
      const wordElement = document.getElementById(`word-${word.id}`);
      wordElement?.classList.add('highlighted');
      currentWordRef.current = { ...word, element: wordElement };
    }
  };

  mediaRef.current?.addEventListener('timeupdate', handleTimeUpdate);
  return () => mediaRef.current?.removeEventListener('timeupdate', handleTimeUpdate);
}, [transcript.words]);

这种方式绕过React状态更新机制,直接操作DOM,彻底避免组件重渲染,但需确保单词DOM元素的id唯一,并处理好清理逻辑。

内容的提问来源于stack exchange,提问作者Hasan Iqbal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 13:55:26