首页首页
前端
非前端
辅助
Github
前端
非前端
辅助
Github
  • 前端基础

    • HTML基础
    • CSS基础
    • JS基础
    • ES6基础
    • HTTP基础
    • 前端缓存
    • 页面性能
    • 数据结构基础
    • 我的文章
  • 前端进阶

    • Vue2基础
    • Vue2进阶
    • Vue3基础
    • Vue3进阶
    • React基础
    • React进阶
    • React18新特性
    • Vue和React对比
    • RN基础
    • RN环境搭建和打包发布
    • 打包工具
    • TS基础
    • Nuxt基础
    • 小程序基础
    • 微前端基础
    • uni-app基础
    • 业务相关
    • 低代码相关
  • 前端代码练习

    • CSS代码练习
    • JS代码练习
    • 算法代码练习
  • 前端代码技巧

    • 工具库
    • 工具函数
    • CSS动画库
    • CSS代码技巧
    • JS代码技巧
    • 项目技巧

1.自动批处理

批处理是指 React 将多个状态更新合并为单个重新渲染,以提高性能。

在 React 17 及之前版本中,只在 React事件处理函数 中进行批处理更新,而在其他情况下(如 promise、setTimeout、原生事件处理程序等)则不会批处理。

举个例子,React 18 之前,渲染次数和更新次数是一样的,即使我们更新了两个状态,每次更新组件也只渲染一次。(代码如下)

import React, { useState } from 'react';

// React 18 之前
const App: React.FC = () => {
  console.log('App组件渲染了!');
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  return (
    <button
      onClick={() => {
        setCount1(count => count + 1);
        setCount2(count => count + 1);
        // 在React事件中被批处理
      }}
    >
      {`count1 is ${count1}, count2 is ${count2}`}
    </button>
  );
};

export default App;

但是,如果我们把状态的更新放在 promise 或者 setTimeout 里面:

每次点击更新两个状态,组件都会渲染两次,不会进行批量更新。(代码如下)

import React, { useState } from 'react';

// React 18 之前
const App: React.FC = () => {
  console.log('App组件渲染了!');
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  return (
    <div
      onClick={() => {
        setTimeout(() => {
          setCount1(count => count + 1);
          setCount2(count => count + 1);
        });
        // 在 setTimeout 中不会进行批处理
      }}
    >
      <div>count1: {count1}</div>
      <div>count2: {count2}</div>
    </div>
  );
};

export default App;

在原生js事件中,情况也是一样,点击更新两个状态,组件都会渲染两次,不会进行批量更新。(代码如下)

import React, { useEffect, useState } from 'react';

// React 18 之前
const App: React.FC = () => {
  console.log('App组件渲染了!');
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  useEffect(() => {
    document.body.addEventListener('click', () => {
      setCount1(count => count + 1);
      setCount2(count => count + 1);
    });
    // 在原生js事件中不会进行批处理
  }, []);
  return (
    <>
      <div>count1: {count1}</div>
      <div>count2: {count2}</div>
    </>
  );
};

export default App;

提示

在 React 18 上面的三个例子只会有一次 render,因为所有的更新都将自动批处理。这样无疑是很好的提高了应用的整体性能。

不过这种情况会在 React 18 中执行两次 render:

await 会破坏自动批处理:任何异步操作(await/Promise/then)都会打断批处理

import React, { useState } from 'react';

// React 18
const App: React.FC = () => {
  console.log('App组件渲染了!');
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  return (
    <div
      onClick={async () => {
        await setCount1(count => count + 1);
        setCount2(count => count + 1);
      }}
    >
      <div>count1: {count1}</div>
      <div>count2: {count2}</div>
    </div>
  );
};

export default App;
  • 注意:flushSync 函数内部的多个 setState 仍然为批量更新,这样可以精准控制哪些不需要的批量更新。
  • 主要优势
    • 减少不必要的渲染:合并多个状态更新为单个重新渲染
    • 提高性能:减少组件渲染次数,特别是对于复杂应用
    • 更一致的更新行为:无论在什么环境下触发更新,行为都一致

2.并发模式(Concurrent Mode)

并发模式是 React 的一种新渲染机制,它允许 React 同时准备多个版本的 UI,并根据优先级选择性地渲染。这与传统的同步渲染(一次完成整个树更新)有本质区别。

React 17 和 React 18 的区别就是:从同步不可中断更新变成了异步可中断更新。

核心概念

    1. 并发渲染(Concurrent Rendering)
    • 可中断渲染:React 可以在渲染过程中暂停、继续或放弃工作
    • 优先级调度:区分高优先级(如用户输入)和低优先级更新(如数据加载)
    • 时间切片:将渲染工作分成小块,避免长时间阻塞主线程
    1. 并发特性(Concurrent Features)

    React 18 提供了一系列基于并发模式的新API:

    • useTransition:标记非紧急更新
    • useDeferredValue:延迟更新某些值
    • <Suspense>:更好地控制加载状态

实际应用场景

  • 输入响应优化:确保输入框即时响应,同时后台处理复杂计算
  • 路由过渡:切换路由时保持当前界面响应,后台加载新路由
  • 大数据渲染:分批渲染大型列表,避免界面冻结
  • 依赖加载:优雅处理代码分割和动态导入

启用并发模式

从React 18开始,默认启用部分并发特性,完整启用方式:

import { createRoot } from 'react-dom/client';

// 替换原来的ReactDOM.render
createRoot(document.getElementById('root')).render(<App />);

3.关于 React 组件的返回值

在 React 17 中,如果你需要返回一个空组件,React只允许返回null。如果你显式的返回了 undefined,控制台则会在运行时抛出一个错误。

在 React 18 中,不再检查因返回 undefined 而导致崩溃。既能返回 null,也能返回 undefined(但是 React 18 的dts文件还是会检查,只允许返回 null,你可以忽略这个类型错误)。

4.新的 Hook

useId

useId 是一个新的Hook,用于生成在客户端和服务端两侧都独一无二的 id,避免激活后两侧内容不匹配。它主要用于需要唯一 id 的,具有集成 API 的组件库。

这个更新不仅解决了一个在 React 17 及更低版本中的存在的问题,而且它会在 React 18 中发挥更重要的作用,因为新的流式服务端渲染响应 HTML 的方式将是无序的,需要独一无二的 id 作为索引。

useDeferredValue

useDeferredValue 允许推迟渲染树的非紧急更新。这和防抖操作非常相似,但是有一些改进。它没有固定的延迟时间,React 会在第一次渲染在屏幕上出现后立即尝试延迟渲染。延迟渲染是可中断的,它不会阻塞用户输入。

useSyncExternalStore

useSyncExternalStore 是一个新的 Hook,允许使用第三方状态管理来支持并发模式,并且能通过对 store 进行强制更新实现数据同步。对第三方数据源的订阅能力的实现上,消除了对 useEffect 的依赖,推荐任何 React 相关的第三方状态管理库使用这个新特性。

最后更新: 2025/5/4 12:01
Prev
React进阶
Next
Vue和React对比