Skip to content

React 18 新特性详解

更新: 8/6/2025 字数: 0 字 时长: 0 分钟

React 18 是 React 的一个重要版本,引入了许多令人兴奋的新功能和改进,旨在提升应用性能和开发体验。本文将详细介绍 React 18 的核心特性。

React 18 主要更新概览

React 18 带来了以下重要更新:

  1. 全新的 Root API
  2. 自动批处理(Automatic Batching)
  3. 并发渲染(Concurrent Rendering)
  4. Suspense 的改进
  5. 新的 Hooks:useId、useTransition、useDeferredValue
  6. 流式服务端渲染(Streaming SSR)
  7. flushSync 强制同步更新
  8. useSyncExternalStore 外部状态同步
  9. 新的 Render API

React 架构演进

在深入了解 React 18 的新特性之前,让我们先了解一下 React 的架构演进历程。从最早的版本到当前的 v18,React 经历了重大的架构变化。

两种核心架构

  1. Stack Reconciler(老架构)

    • 采用不可中断的递归方式更新
    • 在 React v15 及之前版本中使用
  2. Fiber Reconciler(新架构)

    • 采用可中断的遍历方式更新
    • 从 React v16 开始引入

React 版本的四种情况

基于这两种架构和并发更新的启用情况,市面上的 React 版本可以分为四种情况:

  1. 老架构(v15及之前版本)

    • 使用 Stack Reconciler
    • 不支持并发更新
  2. 新架构,未开启并发更新,与情况1行为一致(v16、v17 默认属于这种情况)

    • 使用 Fiber Reconciler
    • 未启用并发更新功能
    • 行为与老架构相似
  3. 新架构,未开启并发更新,但是启用了并发模式和一些新功能(v18 默认属于这种情况)

    • 使用 Fiber Reconciler
    • 未启用并发更新
    • 启用了一些新功能,如 Automatic Batching
  4. 新架构,开启并发模式,开启并发更新

    • 使用 Fiber Reconciler
    • 启用并发更新
    • 可以使用所有并发特性

并发模式与并发更新的关系

在 React 18 中,一个重要的概念澄清是:开启并发模式并不等同于开启并发更新

在 React 17 的一些实验性功能中,开启并发模式确实会开启并发更新。但在 React 18 正式版中,由于官方策略调整,React 不再依赖并发模式来开启并发更新。

换句话说:开启了并发模式,并不一定开启了并发更新!

一句话总结:在 React 18 中,不再有多种模式,而是以是否使用并发特性作为是否开启并发更新的依据。

并发特性指的是开启并发模式后才能使用的特性,比如:

  • useDeferredValue
  • useTransition

全新的 Root API

React 18 引入了新的 Root API,使用 createRoot 替代了旧的 ReactDOM.render 方法:

jsx
// React 18 新方式
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'));
root.render(<App />);

// React 17 旧方式
import ReactDOM from 'react-dom';

ReactDOM.render(<App />, document.getElementById('root'));

新的 Root API 提供了更好的性能和更多功能,特别是在并发渲染方面。

自动批处理(Automatic Batching)

React 18 扩展了自动批处理功能,将多个状态更新合并为单次重新渲染,以提高性能:

jsx
function MyComponent() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    // React 18 会自动批处理这些更新
    setCount(c => c + 1);
    setFlag(f => !f);
    
    // 即使在 Promise 中也会自动批处理
    fetch('/api/data').then(() => {
      setCount(c => c + 1);
      setFlag(f => !f);
    });
  }

  return (
    <div>
      <p>Count: {count}</p>
      <p>Flag: {String(flag)}</p>
      <button onClick={handleClick}>Update</button>
    </div>
  );
}

在 React 17 中,只有在 React 事件处理程序内的更新才会被批处理,而在 Promise、setTimeout 等异步回调中不会。React 18 则会自动批处理所有这些更新。

并发渲染(Concurrent Rendering)

并发渲染是 React 18 的核心特性,它允许 React 同时准备多个版本的 UI,并在后台渲染更新,从而在渲染过程中保持应用的响应性。

并发渲染的优势

  1. 中断渲染:React 可以在需要时中断渲染工作
  2. 优先级调度:高优先级更新可以中断低优先级更新
  3. 保持响应性:即使在大型更新期间,应用也能保持响应

使用并发特性

jsx
import { useTransition } from 'react';

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }

  return (
    <div>
      <button onClick={() => selectTab('about')}>About</button>
      <button onClick={() => selectTab('posts')}>Posts</button>
      <button onClick={() => selectTab('contact')}>Contact</button>
      
      {isPending ? <Spinner /> : <TabContent tab={tab} />}
    </div>
  );
}

Suspense 改进

React 18 对 Suspense 进行了重大改进,使其支持更多场景:

组件级 Suspense

jsx
import { Suspense } from 'react';

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<Spinner />}>
        <ComponentThatSuspends />
      </Suspense>
    </div>
  );
}

流式 HTML 和选择性注水

React 18 改进了服务端渲染,支持流式 HTML 和选择性注水:

jsx
// 服务端
import { renderToPipeableStream } from 'react-dom/server';

function handler(req, res) {
  const { pipe } = renderToPipeableStream(<App />, {
    onShellReady() {
      res.setHeader('content-type', 'text/html');
      pipe(res);
    },
    onError(err) {
      res.statusCode = 500;
      console.error(err);
    }
  });
}

新的 Hooks

React 18 引入了几个新的 Hooks,以支持并发渲染和改进用户体验。

useId Hook

useId 是一个用于生成唯一 ID 的 Hook,特别适用于服务端渲染:

jsx
import { useId } from 'react';

function Checkbox() {
  const id = useId();
  
  return (
    <>
      <label htmlFor={id}>Do you like React?</label>
      <input id={id} type="checkbox" name="react"/>
    </>
  );
}

function App() {
  return (
    <>
      <Checkbox />
      <Checkbox />
    </>
  );
}

useTransition Hook

useTransition 允许你将某些更新标记为过渡更新,从而不会阻塞紧急更新:

jsx
import { useState, useTransition } from 'react';

function App() {
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);

  function handleClick() {
    startTransition(() => {
      setCount(c => c + 1);
    });
  }

  return (
    <div>
      {isPending && <Spinner />}
      <button onClick={handleClick}>{count}</button>
    </div>
  );
}

useDeferredValue Hook

useDeferredValue 允许你推迟重新渲染树的一部分:

jsx
import { useState, useDeferredValue } from 'react';

function SearchResults({ query }) {
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;
  
  return (
    <div className={isStale ? 'opacity-50' : ''}>
      <Results query={deferredQuery} />
    </div>
  );
}

function App() {
  const [query, setQuery] = useState('');
  
  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <SearchResults query={query} />
    </>
  );
}

flushSync 强制同步更新

flushSync 是 React 18 中引入的一个函数,它允许你强制 React 同步刷新所有挂起的工作并更新 DOM。这在某些特殊场景下非常有用,比如在需要立即读取 DOM 更新后的值时。

jsx
import { flushSync } from 'react-dom';

function MyComponent() {
  const [count, setCount] = useState(0);
  
  function handleClick() {
    // 正常的异步更新
    setCount(c => c + 1);
    
    // 在某些场景下需要同步更新
    flushSync(() => {
      setCount(c => c + 1);
    });
    
    // 此时 DOM 已经更新完成
    console.log(document.getElementById('counter').textContent);
  }
  
  return (
    <div>
      <p id="counter">{count}</p>
      <button onClick={handleClick}>Increase</button>
    </div>
  );
}

使用 flushSync 的场景包括:

  1. 与第三方库集成时需要同步更新 DOM
  2. 在事件处理程序中需要立即读取更新后的 DOM 值
  3. 需要确保某些更新在浏览器重排之前完成

需要注意的是,过度使用 flushSync 可能会影响应用性能,因为它会中断并发渲染的优势。

useSyncExternalStore 外部状态同步

useSyncExternalStore 是 React 18 中引入的一个 Hook,用于解决外部状态存储(如 Redux、MobX 或浏览器 API)与 React 并发渲染特性之间的兼容性问题。

jsx
import { useSyncExternalStore } from 'react';

// 外部存储示例
const store = {
  state: { count: 0 },
  listeners: new Set(),
  setState(newState) {
    this.state = { ...this.state, ...newState };
    this.listeners.forEach(listener => listener());
  },
  subscribe(listener) {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  },
  getSnapshot() {
    return this.state;
  }
};

function useStore() {
  return useSyncExternalStore(
    store.subscribe,  // 订阅函数
    store.getSnapshot // 获取快照函数
  );
}

function Counter() {
  const { count } = useStore();
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => store.setState({ count: count + 1 })}>
        Increase
      </button>
    </div>
  );
}

useSyncExternalStore 接收三个参数:

  1. subscribe:订阅函数,用于注册状态变化的监听器
  2. getSnapshot:获取当前状态快照的函数
  3. getServerSnapshot(可选):服务端渲染时获取快照的函数

这个 Hook 确保即使在 React 的并发渲染模式下,外部状态也能正确地与组件同步。

流式服务端渲染(Streaming SSR)

React 18 引入了两个新的服务端 API,支持流式渲染:

renderToPipeableStream

用于 Node 环境:

jsx
import { renderToPipeableStream } from 'react-dom/server';
import App from './App';

function handler(req, res) {
  res.setHeader('Content-Type', 'text/html');
  
  const { pipe } = renderToPipeableStream(
    <App />,
    {
      onShellReady() {
        // 第一批 HTML 准备就绪
        pipe(res);
      },
      onShellError(error) {
        // 发生错误时的处理
        res.statusCode = 500;
        res.send('<h1>An error occurred</h1>');
      },
      onAllReady() {
        // 所有内容准备就绪(可选)
      },
      onError(err) {
        // 记录错误
        console.error(err);
      }
    }
  );
}

renderToReadableStream

用于边缘运行时(如 Cloudflare Workers):

jsx
import { renderToReadableStream } from 'react-dom/server';
import App from './App';

export default async function handler(request) {
  const stream = await renderToReadableStream(
    <App />,
    {
      onError(err) {
        console.error(err);
      }
    }
  );
  
  return new Response(stream, {
    headers: { 'Content-Type': 'text/html' }
  });
}

选择性注水(Selective Hydration)

React 18 支持选择性注水,允许组件在准备就绪时立即注水,而不是等待整个应用:

jsx
// 在服务端渲染时,React 会自动将 Suspense 边界注入到 HTML 中
// 在客户端,React 会优先注水交互式组件

function App() {
  return (
    <div>
      <Sidebar />
      <Suspense fallback={<Spinner />}>
        <Content />
      </Suspense>
      <Suspense fallback={<Spinner />}>
        <Comments />
      </Suspense>
    </div>
  );
}

与 React 17 的对比

特性React 17React 18
Root APIReactDOM.rendercreateRoot
自动批处理仅限事件处理程序所有更新
并发渲染不支持支持
Suspense仅限 lazy支持更多场景
流式 SSR不支持支持
选择性注水不支持支持
flushSync不支持支持
useSyncExternalStore不支持支持

升级到 React 18

升级到 React 18 通常很简单,但需要注意以下几点:

  1. 更新 Root API:

    jsx
    // 从
    ReactDOM.render(<App />, document.getElementById('root'));
    
    // 改为
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(<App />);
  2. 处理自动批处理可能导致的行为变化

  3. 更新相关依赖项到兼容版本

性能优化建议

  1. 使用 useTransition 处理大型更新

    jsx
    const [isPending, startTransition] = useTransition();
  2. 利用 useDeferredValue 优化搜索

    jsx
    const deferredQuery = useDeferredValue(query);
  3. 适当使用 Suspense 分割加载状态

    jsx
    <Suspense fallback={<Spinner />}>
      <ExpensiveComponent />
    </Suspense>

总结

React 18 是 React 发展历程中的一个重要里程碑,引入了并发渲染、自动批处理、新的 Hooks 等一系列重要特性。这些更新不仅提升了应用性能,还改善了开发体验。通过采用 React 18 的新特性,开发者可以构建更流畅、更响应迅速的用户界面。

升级到 React 18 是值得的,特别是对于需要处理复杂交互和大量数据的应用。并发渲染和流式 SSR 等特性可以帮助应用在保持响应性的同时处理更复杂的 UI 更新。