其他性能优化常考点

一、其他性能优化常考点

这节是”散装知识点”——每个都能独立出一道题,但深度不如 memo 三兄弟。按 2026 频率排序。

1.1 React.lazy + Suspense —— 代码分割

 
 
 
// 不是一开始加载所有页面代码,而是"用到才加载"
 
 
 
const UserProfile = React.lazy(() => import("./UserProfile"));
 
 
 
//                              ↑ 动态 import() → webpack 自动分包
 
 
 
 
 
function App() {
 
 
 
  return (
 
 
 
    <Suspense fallback={<div>加载中...</div>}>
 
 
 
      {/* UserProfile 的代码在首次渲染时才去下载 */}
 
 
 
      <UserProfile />
 
 
 
    </Suspense>
 
 
 
  );
 
 
 
}
 
 
 

原理React.lazy 接收一个返回 Promise 的函数(() => import('./X'))。React 渲染到它时发现组件还没加载 → 往上找最近的 <Suspense> → 显示 fallback → 等 Promise resolve → 渲染真实组件。

一句话讲清:“代码分割的核心是按路由拆分 bundle——用户访问 /settings 时才加载 Settings 页面的 JS,首屏只加载必需代码。配合 React.lazy + Suspense 或路由级的 lazy() 实现。“

1.2 虚拟列表 —— 大数据量渲染优化




graph TD



  subgraph VirtualList[虚拟列表原理]



    TOP[已滚过的内容<br/>不渲染<br/>padding-top撑高度]



    VIEW[可视区域<br/>有真实DOM<br/>item 47 item 48 item 49]



    BOT[待滚动到的内容<br/>不渲染<br/>padding-bottom撑高度]



  end



  TOP --> VIEW --> BOT





  subgraph Compare[对比]



    Normal[普通列表: 10000条→10000 DOM节点→卡死]



    Virtual[虚拟列表: 10000条→~20 DOM节点→流畅]



  end



常用库:react-window(轻量)、react-virtualized(功能全)。不说库名也行——把”只渲染可见区域 + padding 撑高度”这个原理讲清楚就够了。

1.3 防抖与节流 —— 高频事件优化

防抖(Debounce)节流(Throttle)
行为你一直点我就一直重置计时器——等你停手了再执行你一直点我最多每 N 毫秒执行一次
比喻电梯关门:有人进来就重新计时,直到没人了才关门地铁闸机:过一个人要等一下才能过下一个
场景搜索输入、窗口 resize滚动事件、按钮连点
 
 
 
// 防抖 Hook —— 搜索框场景
 
 
 
function useDebounce<T>(value: T, delay: number): T {
 
 
 
  const [debouncedValue, setDebouncedValue] = useState(value);
 
 
 
 
 
  useEffect(() => {
 
 
 
    const timer = setTimeout(() => setDebouncedValue(value), delay);  // 延迟 delay 后更新
 
 
 
    return () => clearTimeout(timer);  // value 又变了 → 清除上一个定时器 → 重新计时
 
 
 
  }, [value, delay]);
 
 
 
 
 
  return debouncedValue;
 
 
 
}
 
 
 
 
 
// 使用:用户停止输入 300ms 后才发请求
 
 
 
function SearchBox() {
 
 
 
  const [query, setQuery] = useState("");
 
 
 
  const debouncedQuery = useDebounce(query, 300);
 
 
 
 
 
  useEffect(() => {
 
 
 
    if (debouncedQuery) fetchResults(debouncedQuery);  // 300ms 没输入了才发请求
 
 
 
  }, [debouncedQuery]);
 
 
 
}
 
 
 

1.4 key 的正确使用

这个看似简单,一问”为什么不能用 index 做 key”——80% 的人说不清楚原理。

 
 
 
// ❌ 用 index 做 key —— 列表顺序变了就出 bug
 
 
 
{items.map((item, index) => <Item key={index} {...item} />)}
 
 
 
 
 
// ✅ 用稳定的唯一标识
 
 
 
{items.map(item => <Item key={item.id} {...item} />)}
 
 
 

为什么 index 有问题? React 用 key 来判断”哪个元素变了”。如果用 index:




graph LR



  subgraph Before[原来]



    B0[key=0 A] --> B1[key=1 B] --> B2[key=2 C]



  end



  subgraph After[头部插入D后]



    A0[key=0 D] --> A1[key=1 A] --> A2[key=2 B] --> A3[key=3 C]



  end



  Before -.->|React比对| After



  After -.->|key=0 内容A→D 更新DOM<br/>key=1 内容B→A 更新DOM<br/>key=2 内容C→B 更新DOM<br/>多了key=3 新增DOM| R[所有元素被更新<br/>A/B/C本身没变]






graph LR



  subgraph Before[原来]



    B0[key=a A] --> B1[key=b B] --> B2[key=c C]



  end



  subgraph After[头部插入D后]



    A0[key=d D] --> A1[key=a A] --> A2[key=b B] --> A3[key=c C]



  end



  After -.->|React比对| R[多了key=d 新增DOM ✅<br/>key=a/b/c没变 复用DOM ✅<br/>只有D创建了DOM<br/>A/B/C全部复用 高效]



1.5 状态提升 vs 状态下沉

 
 
 
状态提升:把 state 放到"需要它的组件的最近公共祖先"
 
 
 
  → 问题:祖先更新 → 所有后代都重渲染(即使不关心这个 state)
 
 
 
 
 
状态下沉:把 state 尽量放到"实际使用它的组件"里
 
 
 
  → 好处:state 更新只影响真正关心它的那个小组件
 
 
 
 
 
 
// ❌ 状态提升过度——input 的 value 变化导致整个 App 重渲染
 
 
 
function App() {
 
 
 
  const [search, setSearch] = useState("");          // state 在顶层
 
 
 
  return (
 
 
 
    <div>
 
 
 
      <Header />           {/* 不关心 search,但也会重渲染 */}
 
 
 
      <SearchInput value={search} onChange={setSearch} />
 
 
 
      <Content />          {/* 不关心 search,但也会重渲染 */}
 
 
 
      <Footer />           {/* 不关心 search,但也会重渲染 */}
 
 
 
    </div>
 
 
 
  );
 
 
 
}
 
 
 
 
 
// ✅ 状态下沉——search 只影响 SearchInput
 
 
 
function App() {
 
 
 
  return (
 
 
 
    <div>
 
 
 
      <Header />
 
 
 
      <SearchInput />      {/* state 在 SearchInput 内部 */}
 
 
 
      <Content />
 
 
 
      <Footer />
 
 
 
    </div>
 
 
 
  );
 
 
 
}
 
 
 

1.6 useTransition / useDeferredValue —— React 18 并发特性

useTransitionuseDeferredValue
做什么把某个 state 更新标记为”低优先级”让某个值的更新”滞后”——先用旧值显示
返回值[isPending, startTransition]延迟后的值
场景Tab 切换、路由跳转搜索建议——用户输入时列表先不动
 
 
 
// useTransition:Tab 切换时先显示旧 Tab,新 Tab 的内容"稍后追上"
 
 
 
function Tabs() {
 
 
 
  const [tab, setTab] = useState("home");
 
 
 
  const [isPending, startTransition] = useTransition();
 
 
 
 
 
  function selectTab(nextTab: string) {
 
 
 
    startTransition(() => setTab(nextTab));  // 标记为低优先级——别挡用户操作
 
 
 
  }
 
 
 
  return (
 
 
 
    <div>
 
 
 
      <button onClick={() => selectTab("home")}>Home</button>
 
 
 
      <button onClick={() => selectTab("profile")}>Profile</button>
 
 
 
      {isPending && <Spinner />}  {/* 新 Tab 还在准备中——显示 loading */}
 
 
 
      <TabContent tab={tab} />    {/* 这是慢组件——React 可以中断它的渲染 */}
 
 
 
    </div>
 
 
 
  );
 
 
 
}
 
 
 
 
 
// useDeferredValue:搜索框——用户打字时不阻塞输入
 
 
 
function SearchPage({ query }: { query: string }) {
 
 
 
  const deferredQuery = useDeferredValue(query);  // query 变了会滞后更新
 
 
 
  return (
 
 
 
    <>
 
 
 
      <input value={query} />                     {/* 输入永远流畅 */}
 
 
 
      <SearchResults query={deferredQuery} />     {/* 结果列表可以"慢一拍" */}
 
 
 
    </>
 
 
 
  );
 
 
 
}
 
 
 

速记卡(面试闪卡)

Q1:一句话讲清「其他性能优化常考点」到底是什么?

A:除 memo 三兄弟,React 性能优化还有六块:代码分割、虚拟列表、防抖节流、key 用法、状态升降、并发特性。

Q2:1.1 代码分割 + 1.2 虚拟列表 —— 怎么理解? —— 怎么理解?

A:React.lazy + Suspense 做代码分割:组件不进主 bundle,用到才动态 import,首屏更快,加载时显示 fallback。虚拟列表只渲染视口内十几行,滚到哪渲哪,DOM 数量恒定。就像火车车窗——只看到窗外那几节,不用摆整列车在眼前。

Q3:1.3 防抖节流 + 1.4 key 的正确使用 —— 怎么理解? —— 怎么理解?

A:防抖:事件停 N 毫秒后才执行(搜索输完再发请求);节流:每 N 毫秒最多一次(滚动、拖拽)。key 要用稳定唯一 id,别用数组下标——用 index 当 key,列表增删会让 React 复用错 DOM、状态错乱。

Q4:1.5 状态提升 vs 下沉 —— 怎么理解? —— 怎么理解?

A:状态提升:兄弟组件共享数据时把 state 提到最近公共父。状态下沉(colocation):把只属于某子组件的状态往下放,避免无关节件跟着重渲染。原则:state 放在”真正需要它的最小组件”,既共享又不浪费渲染。

Q5:1.6 useTransition / useDeferredValue —— 怎么理解? —— 怎么理解?

A:React 18 并发特性。useTransition 把”非紧急更新”标记成可中断——输入时旧结果先留、新结果后台算,界面不卡。useDeferredValue 返回”延迟版”值,紧急渲染用旧值、后台算新值。本质:让紧急交互(打字)优先,耗时渲染让路。

Q6:核心速记主线有哪些?

  • 代码分割:lazy + Suspense,首屏更快

  • 虚拟列表:只渲可见区,DOM 恒定

  • 防抖等停再跑、节流匀速跑;key 用稳定 id

  • 状态下沉减少重渲染;并发特性让紧急交互优先

口诀

A:性能优化六把刀,

lazy 分割首屏高;

防抖节流限高频,

key 用 id 下标抛。

相关链接