React 18 并发模式:Fiber 架构与时间切片

React 18 并发模式:Fiber 架构与时间切片

作者按: 本文基于 React 18.3.1 源码深度解析并发模式的底层实现原理,结合大量流程图、对比表格和实战代码,帮助开发者彻底理解 Fiber 架构与时间切片机制。


📚 目录

  1. [引言 - React 18 与并发模式概述](#引言 - React 18 与并发模式概述)
  2. [Fiber 架构深度解析](#Fiber 架构深度解析)
  3. 时间切片机制
  4. 并发模式核心特性
  5. 源码解析与实战
  6. 性能优化与最佳实践
  7. 总结与展望

1. 引言 - React 18 与并发模式概述

1.1 什么是并发模式?

并发模式(Concurrent Mode) 是 React 18 引入的一组新功能,它允许 React 中断恢复渲染工作,从而实现:

  • 优先级调度:高优先级任务可以打断低优先级任务
  • 可中断渲染:长时间任务可以被分割成多个小任务
  • 并发渲染:同时准备多个版本的 UI
  • 更好的用户体验:应用始终保持响应

1.2 并发模式 vs 传统渲染

并发可中断渲染
用户交互
时间切片 5ms
响应用户
继续渲染
完成渲染
传统同步渲染
用户交互
阻塞渲染 60ms+
页面响应

对比分析:

特性 传统模式(React 17) 并发模式(React 18)
渲染方式 同步、不可中断 异步、可中断
任务调度 FIFO(先进先出) 基于优先级调度
用户输入响应 可能被长时间渲染阻塞 始终保持响应
内存占用 较低 稍高(需要保存中断状态)
适用场景 简单应用 复杂交互、大数据渲染

2. Fiber 架构深度解析

2.1 从 Stack Reconciler 到 Fiber

在 React 15 及之前,React 使用 Stack Reconciler (栈协调器)进行渲染。这种方式的致命缺陷是:一旦开始渲染,就无法中断,直到整个虚拟 DOM 树遍历完成
React 18 Fiber Reconciler
setState
每个Fiber节点 5ms
时间片用完暂停
处理高优先级任务
空闲时继续
渐进式渲染
React 15 Stack Reconciler
setState
递归遍历整个树
无法中断
长时间占用主线程
用户输入被阻塞

核心问题对比:

问题 Stack Reconciler Fiber Reconciler
递归调用栈 深度递归,容易栈溢出 链表结构,无递归
中断能力 ❌ 无法中断 ✅ 可随时中断/恢复
任务优先级 ❌ 无优先级概念 ✅ Lane 模型优先级
增量渲染 ❌ 一次性渲染 ✅ 分帧渲染

2.2 Fiber 数据结构

Fiber 不仅是一个数据结构,更是 React 16+ 的工作单元。每个 Fiber 节点对应一个 React 元素,包含组件的状态、副作用等信息。

源码位置: packages/react-reconciler/src/ReactInternalTypes.js

typescript 复制代码
// React 18.3.1 Fiber 节点结构(简化版)
type Fiber = {
  // === 标签类型 ===
  tag: WorkTag,              // 组件类型(函数组件/类组件/原生标签等)
  
  // === 树形结构 ===
  return: Fiber | null,      // 父节点(原 parent)
  child: Fiber | null,       // 第一个子节点
  sibling: Fiber | null,     // 下一个兄弟节点
  index: number,             // 在父节点中的索引
  
  // === 状态管理 ===
  memoizedState: any,        // 上次渲染的 state
  memoizedProps: any,        // 上次渲染的 props
  updateQueue: mixed,        // 更新队列(state/hooks 队列)
  
  // === 副作用 ===
  flags: Flags,              // 副作用标记(原 effectTag)
  subtreeFlags: Flags,       // 子树副作用标记
  deletions: Array<Fiber> | null, // 要删除的子节点
  
  // === 调度相关 ===
  lanes: Lanes,              // 优先级车道
  childLanes: Lanes,         // 子树优先级
  
  // === 双缓存 ===
  alternate: Fiber | null,   // current 树和 workInProgress 树的对应节点
  
  // === 其他 ===
  key: string | null,        // React key
  elementType: any,          // 元素类型
  type: any,                 // 组件构造函数/标签名字符串
  ref: mixed,                // React ref
};

Fiber 双缓存机制图解:
WorkInProgress 树(构建中)
Current 树(已渲染)
alternate
alternate
alternate
完成后交换
Fiber Node A
Fiber Node B
Fiber Node C
Fiber Node A'
Fiber Node B'
Fiber Node C'

2.3 Fiber 工作原理

Fiber 的工作循环分为两个阶段:

阶段一:Render 阶段(可中断)

调度更新
开始工作循环
执行工作循环
处理 Fiber 节点
时间片用完?
空闲时恢复
节点处理完成
继续下一个
Render 阶段完成
Schedule
Perform
WorkLoop
继续处理
ShouldYield
|是|
让出主线程
|否|
Complete
CommitReady
可中断点

高优先级任务可插入

源码位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

javascript 复制代码
// React 18.3.1 工作循环核心逻辑(简化)
function workLoopConcurrent() {
  // 只要还有工作要做,且未超时
  while (workInProgress !== null && !shouldYield()) {
    // 执行当前 Fiber 节点的工作
    performUnitOfWork(workInProgress);
  }
}

// 检查是否应该让出主线程
function shouldYield() {
  // 获取当前时间
  const currentTime = getCurrentTime();
  // 如果已经超过帧预算(默认 5ms),返回 true
  return currentTime >= deadline;
}

// 处理单个 Fiber 节点
function performUnitOfWork(unitOfWork: Fiber): void {
  // 1. 开始阶段:处理 props、构建子 Fiber 节点
  const next = beginWork(unitOfWork);
  
  if (next === null) {
    // 没有子节点,进入完成阶段
    completeUnitOfWork(unitOfWork);
  } else {
    // 继续处理子节点
    workInProgress = next;
  }
}
阶段二:Commit 阶段(不可中断)

Render 阶段完成
Before Mutation
Mutation
Layout
更新 DOM
执行 useEffect
浏览器绘制

源码位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

javascript 复制代码
// React 18.3.1 Commit 阶段入口
function commitRoot(root) {
  const finishedWork = root.finishedWork;
  const lanes = root.finishedLanes;
  
  // 1. Before Mutation 阶段
  // - 执行 getSnapshotBeforeUpdate
  // - 调度 useEffect
  commitBeforeMutationEffects(finishedWork);
  
  // 2. Mutation 阶段
  // - 真正操作 DOM(插入、更新、删除)
  // - 执行 useLayoutEffect 清理函数
  commitMutationEffects(finishedWork);
  
  // 3. Layout 阶段
  // - 执行 useLayoutEffect 回调
  // - 执行 ref 回调
  commitLayoutEffects(finishedWork);
  
  // 重置工作树
  root.current = finishedWork;
}

React 渲染流程完整图:
高优先级任务
setState/触发更新
创建更新对象
调度更新
Render 阶段

可中断
构建 WorkInProgress 树
Diff 算法
收集副作用
Commit 阶段

不可中断
操作真实 DOM
浏览器绘制
渲染完成
中断
处理高优先级


3. 时间切片机制

3.1 调度器(Scheduler)原理

React 18 使用独立的 Scheduler 模块 来实现时间切片,它基于 MessageChannelrequestIdleCallback 思想实现。

源码位置: packages/scheduler/src/forks/Scheduler.js

javascript 复制代码
// React 18.3.1 Scheduler 核心逻辑(简化)
let taskQueue = [];
let currentTime = -1;
let schedulerHostCallback = null;
let isSchedulerPaused = false;

// 调度任务
function scheduleCallback(priorityLevel, callback, options) {
  const currentTime = getCurrentTime();
  
  // 创建任务对象
  const newTask = {
    id: taskIdCounter++,
    callback,              // 回调函数
    priorityLevel,         // 优先级
    startTime,             // 开始时间(延迟任务)
    expirationTime,        // 过期时间
    sortIndex: -1,         // 排序索引
  };
  
  // 根据优先级排序
  push(taskQueue, newTask);
  
  // 请求调度
  requestHostCallback(flushWork);
  
  return newTask;
}

// 执行工作循环
function flushWork(hasTimeRemaining, initialTime) {
  const currentTime = initialTime;
  
  // 持续执行任务,直到队列为空或超时
  return workLoop(hasTimeRemaining, initialTime);
}

function workLoop(hasTimeRemaining, initialTime) {
  let currentTime = initialTime;
  currentTask = peek(taskQueue);
  
  while (currentTask !== null) {
    if (
      !hasTimeRemaining ||  // 没有时间剩余
      shouldYieldToHost()   // 应该让出控制权
    ) {
      // 时间片用完,中断任务
      break;
    }
    
    // 执行任务回调
    const callback = currentTask.callback;
    if (callback !== null) {
      currentTask.callback = null;
      const continuationCallback = callback(currentTask.expirationTime);
      
      // 任务未完成,重新入队
      if (typeof continuationCallback === 'function') {
        currentTask.callback = continuationCallback;
        return true; // 还有工作
      }
    }
    
    // 移除已完成任务
    pop(taskQueue);
    currentTask = peek(taskQueue);
  }
  
  // 更多工作待处理?
  if (currentTask !== null) {
    return true;
  }
  
  return false;
}

// 检查是否应该让出主线程
function shouldYieldToHost() {
  const timeElapsed = getCurrentTime() - startTime;
  if (timeElapsed < frameInterval) {
    return false;
  }
  
  // 检查是否有更高优先级的任务(如用户输入)
  if (needsPaint) {
    return true;
  }
  
  return true;
}

// 通过 MessageChannel 实现异步调度
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = performWorkUntilDeadline;

function requestHostCallback(callback) {
  schedulerHostCallback = callback;
  port.postMessage(null);
}

function performWorkUntilDeadline() {
  if (schedulerHostCallback !== null) {
    currentTime = getCurrentTime();
    const hasTimeRemaining = true;
    const hasMoreWork = schedulerHostCallback(hasTimeRemaining, currentTime);
    
    if (hasMoreWork) {
      // 还有工作,继续调度
      port.postMessage(null);
    } else {
      schedulerHostCallback = null;
    }
  }
}

3.2 任务优先级

React 使用 Lane 模型(车道模型)来管理优先级,每个更新都有一个或多个 "lanes"(车道)。

源码位置: packages/react-reconciler/src/ReactFiberLane.js

javascript 复制代码
// React 18.3.1 优先级定义(简化)
const TotalLanes = 31;

// 同步优先级(最高,如 ReactDOM.render)
const SyncLane: Lane = 0b0000000000000000000000000000001;

// 输入事件(如点击、输入)
const InputContinuousLane: Lane = 0b0000000000000000000000000000100;

// 默认优先级(如 useEffect)
const DefaultLane: Lane = 0b0000000000000000000000000010000;

// 过渡动画(较低优先级)
const TransitionLanes: Lanes = 0b0000000000000000000001111110000;

// 空闲时执行(最低优先级)
const IdleLane: Lane = 0b0100000000000000000000000000000;

// 获取最高优先级车道
function getHighestPriorityLanes(lanes: Lanes): Lanes {
  return lanes & -lanes; // 取最低位的 1
}

// 检查是否被更高优先级打断
function includesHigherPriority(lanes: Lanes, renderLanes: Lanes): boolean {
  return (lanes & renderLanes) !== lanes;
}

优先级对比表:

优先级 Lane 值 场景示例 是否可中断 超时时间
SyncLane 0b0001 ReactDOM.render、初次渲染 ❌ 不可中断 立即
InputContinuousLane 0b0100 用户输入(onChange) ❌ 几乎不可中断 ~250ms
DefaultLane 0b1000 setState、useEffect ✅ 可中断 ~5s
TransitionLane 0b11110000 startTransition ✅ 可中断 ~5s
IdleLane 0b0100... useDeferredValue ✅ 可中断 无限制

3.3 可中断渲染演示

示例:模拟时间切片

javascript 复制代码
import { useState, useEffect, useRef } from 'react';

function TimeSlicingDemo() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);
  const expensiveTaskRef = useRef(null);

  // 模拟耗时任务(不使用时间切片)
  function runHeavyTaskWithoutSlicing() {
    console.time('Without Time Slicing');
    const result = [];
    for (let i = 0; i < 100000; i++) {
      result.push({
        id: i,
        value: Math.random(),
        computed: Math.sqrt(i) * Math.sin(i),
      });
    }
    setItems(result);
    console.timeEnd('Without Time Slicing');
  }

  // 使用时间切片(React 18 自动处理)
  function runHeavyTaskWithSlicing() {
    console.time('With Time Slicing');
    setItems([]); // 清空
    
    let currentIndex = 0;
    const batchSize = 5000; // 每批处理 5000 条
    const totalItems = 100000;
    
    function processBatch() {
      const batch = [];
      const end = Math.min(currentIndex + batchSize, totalItems);
      
      for (let i = currentIndex; i < end; i++) {
        batch.push({
          id: i,
          value: Math.random(),
          computed: Math.sqrt(i) * Math.sin(i),
        });
      }
      
      setItems(prev => [...prev, ...batch]);
      currentIndex = end;
      
      if (currentIndex < totalItems) {
        // 让出控制权,浏览器有机会处理其他任务
        requestIdleCallback(processBatch, { timeout: 1000 });
      } else {
        console.timeEnd('With Time Slicing');
      }
    }
    
    processBatch();
  }

  return (
    <div style={{ padding: '20px' }}>
      <h1>React 18 时间切片演示</h1>
      <p>当前计数: {count}</p>
      
      <div style={{ marginBottom: '20px' }}>
        <button onClick={() => setCount(c => c + 1)}>
          增加计数(测试响应性)
        </button>
      </div>
      
      <div style={{ marginBottom: '20px' }}>
        <button onClick={runHeavyTaskWithoutSlicing}>
          运行耗时任务(无切片)- 会阻塞
        </button>
        <button onClick={runHeavyTaskWithSlicing} style={{ marginLeft: '10px' }}>
          运行耗时任务(有时间切片)- 不阻塞
        </button>
      </div>
      
      <p>已生成 {items.length} 条数据</p>
      
      {items.length > 0 && (
        <div style={{ marginTop: '20px', maxHeight: '200px', overflow: 'auto' }}>
          <table>
            <thead>
              <tr>
                <th>ID</th>
                <th>值</th>
                <th>计算结果</th>
              </tr>
            </thead>
            <tbody>
              {items.slice(0, 100).map(item => (
                <tr key={item.id}>
                  <td>{item.id}</td>
                  <td>{item.value.toFixed(4)}</td>
                  <td>{item.computed.toFixed(4)}</td>
                </tr>
              ))}
            </tbody>
          </table>
          {items.length > 100 && <p>... 还有 {items.length - 100} 条</p>}
        </div>
      )}
    </div>
  );
}

export default TimeSlicingDemo;

时间切片对比效果:
有时间切片
让出
让出
让出
0-5ms
用户输入响应
5-10ms
浏览器绘制
10-15ms
处理事件
继续...
无时间切片
阻塞
阻塞
阻塞
0ms
100ms
200ms
300ms
完成


4. 并发模式核心特性

4.1 Concurrent Features

React 18 提供了三个主要的并发特性:

特性 API 用途 优先级
Transitions startTransition 标记非紧急更新 Transition
Deferred Value useDeferredValue 延迟更新不重要的值 Transition/Idle
Suspense <Suspense> 数据加载时的后备 UI 默认

4.2 Transitions

Transitions 允许将更新标记为"非紧急",React 可以中断它们以处理更重要的更新。

源码位置: packages/react-reconciler/src/ReactFiberWorkLoop.js

javascript 复制代码
// React 18.3.1 startTransition 实现(简化)
function startTransition(callback) {
  const previousPriority = getCurrentUpdatePriority();
  
  try {
    // 降低优先级到 TransitionLane
    setCurrentUpdatePriority(TransitionPriority);
    callback();
  } finally {
    setCurrentUpdatePriority(previousPriority);
  }
}

实战示例:搜索输入框

javascript 复制代码
import { useState, useTransition, useMemo } from 'react';

function SearchWithTransition() {
  const [searchTerm, setSearchTerm] = useState('');
  const [isPending, startTransition] = useTransition();
  
  // 模拟大数据集
  const allItems = useMemo(() => {
    return Array.from({ length: 50000 }, (_, i) => ({
      id: i,
      name: `Item ${i}`,
      description: `This is item number ${i} with some description`,
      tags: [`tag${i % 10}`, `tag${i % 5}`],
    }));
  }, []);
  
  // 紧急更新:输入框(立即响应)
  const handleInputChange = (e) => {
    setSearchTerm(e.target.value);
  };
  
  // 非紧急更新:搜索结果(可以延迟)
  const filteredItems = useMemo(() => {
    if (!searchTerm) return allItems;
    
    const term = searchTerm.toLowerCase();
    return allItems.filter(item => 
      item.name.toLowerCase().includes(term) ||
      item.description.toLowerCase().includes(term) ||
      item.tags.some(tag => tag.toLowerCase().includes(term))
    );
  }, [searchTerm, allItems]);
  
  return (
    <div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
      <h1>React 18 Transitions 示例</h1>
      
      {/* 搜索框 - 紧急更新 */}
      <div style={{ marginBottom: '20px' }}>
        <input
          type="text"
          value={searchTerm}
          onChange={handleInputChange}
          placeholder="搜索 50000 条数据..."
          style={{
            width: '100%',
            padding: '10px',
            fontSize: '16px',
            border: '2px solid #ccc',
            borderRadius: '4px',
          }}
        />
        <p style={{ color: '#666', fontSize: '14px' }}>
          输入时保持响应(紧急更新),搜索结果延迟渲染(非紧急更新)
        </p>
      </div>
      
      {/* 加载指示器 */}
      {isPending && (
        <div style={{
          padding: '10px',
          background: '#fff3cd',
          borderRadius: '4px',
          marginBottom: '10px'
        }}>
          ⏳ 正在搜索...
        </div>
      )}
      
      {/* 搜索结果 - 使用 Transition 优化 */}
      <div style={{
        border: '1px solid #ddd',
        borderRadius: '4px',
        padding: '10px',
        height: '400px',
        overflowY: 'auto'
      }}>
        <p>找到 {filteredItems.length} 条结果</p>
        
        {filteredItems.slice(0, 100).map(item => (
          <div
            key={item.id}
            style={{
              padding: '10px',
              borderBottom: '1px solid #eee',
              transition: 'background 0.2s'
            }}
          >
            <h3>{item.name}</h3>
            <p>{item.description}</p>
            <div>
              {item.tags.map(tag => (
                <span
                  key={tag}
                  style={{
                    background: '#e0f7fa',
                    padding: '2px 8px',
                    borderRadius: '12px',
                    fontSize: '12px',
                    marginRight: '5px'
                  }}
                >
                  {tag}
                </span>
              ))}
            </div>
          </div>
        ))}
        
        {filteredItems.length > 100 && (
          <p style={{ textAlign: 'center', color: '#999' }}>
            ... 还有 {filteredItems.length - 100} 条结果(为性能仅显示前 100 条)
          </p>
        )}
      </div>
    </div>
  );
}

export default SearchWithTransition;

Transition 工作流程:
搜索组件 React 输入框 用户 搜索组件 React 输入框 用户 之前的搜索被中断 搜索 "ab" 被中断 输入 "a" setState("a") - 紧急 立即更新输入框 ✅ 输入 "ab" setState("ab") - 紧急 立即更新输入框 ✅ 开始搜索 "ab" - 非紧急 返回结果 更新列表 输入 "abc" setState("abc") - 紧急 立即更新输入框 ✅ 开始搜索 "abc" - 非紧急 返回结果 更新列表

性能对比:

指标 不使用 Transition 使用 Transition
输入响应 可能卡顿 始终流畅
搜索延迟 ~100ms(可接受)
用户体验 😞 差 😊 好
CPU 占用 集中爆发 平滑分散

4.3 Suspense 增强

React 18 的 Suspense 支持并发渲染,可以配合 Transitions 使用。

javascript 复制代码
import { useState, Suspense, useTransition } from 'react';

// 模拟异步数据获取
function fetchData(id) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ id, name: `Data ${id}`, content: `Content for ${id}` });
    }, 2000);
  });
}

// 数据组件
function DataComponent({ id }) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  
  if (!data) {
    throw fetchData(id).then(setData).catch(setError);
  }
  
  if (error) {
    return <div style={{ color: 'red' }}>错误: {error.message}</div>;
  }
  
  return (
    <div style={{
      padding: '20px',
      border: '1px solid #ddd',
      borderRadius: '8px',
      background: '#f9f9f9'
    }}>
      <h2>{data.name}</h2>
      <p>{data.content}</p>
      <p style={{ color: '#666', fontSize: '14px' }}>
        加载时间: {new Date().toLocaleTimeString()}
      </p>
    </div>
  );
}

// 使用 Suspense 的容器组件
function SuspenseDemo() {
  const [selectedId, setSelectedId] = useState(1);
  const [isPending, startTransition] = useTransition();
  
  const handleSelect = (id) => {
    // 使用 Transition 切换数据,保持 UI 响应
    startTransition(() => {
      setSelectedId(id);
    });
  };
  
  return (
    <div style={{ padding: '20px' }}>
      <h1>React 18 Suspense + Transitions</h1>
      
      <div style={{ marginBottom: '20px' }}>
        {[1, 2, 3, 4, 5].map(id => (
          <button
            key={id}
            onClick={() => handleSelect(id)}
            disabled={isPending}
            style={{
              padding: '10px 20px',
              margin: '0 5px',
              background: selectedId === id ? '#2196F3' : '#e0e0e0',
              color: selectedId === id ? 'white' : 'black',
              border: 'none',
              borderRadius: '4px',
              cursor: 'pointer',
              opacity: isPending ? 0.6 : 1,
            }}
          >
            数据 {id}
          </button>
        ))}
      </div>
      
      {isPending && (
        <div style={{
          padding: '10px',
          background: '#fff3cd',
          borderRadius: '4px',
          marginBottom: '10px'
        }}>
          ⏳ 加载中...
        </div>
      )}
      
      <Suspense fallback={
        <div style={{
          padding: '40px',
          textAlign: 'center',
          background: '#f5f5f5',
          borderRadius: '8px'
        }}>
          <p style={{ fontSize: '18px', color: '#666' }}>
            🔄 加载中,请稍候...
          </p>
        </div>
      }>
        <DataComponent key={selectedId} id={selectedId} />
      </Suspense>
      
      <div style={{ marginTop: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
        <h3>💡 说明:</h3>
        <ul style={{ lineHeight: '1.8' }}>
          <li>点击按钮切换数据时,使用 <code>startTransition</code></li>
          <li><code>Suspense</code> 捕获 Promise 并显示 fallback</li>
          <li>切换期间保持响应,不会阻塞 UI</li>
          <li>旧内容保持显示,直到新数据加载完成</li>
        </ul>
      </div>
    </div>
  );
}

export default SuspenseDemo;

Suspense 数据流图:


用户点击
startTransition
降低优先级
触发更新
Render Phase
数据是否就绪?
抛出 Promise
Suspense 捕获
显示 Fallback
等待数据
数据返回
重新渲染
渲染数据
Commit 阶段
更新 DOM


5. 源码解析与实战

5.1 关键源码分析

5.1.1 更新调度流程

源码路径: packages/react-reconciler/src/ReactFiberWorkLoop.js

javascript 复制代码
// React 18.3.1 更新调度入口
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
  // 获取下一个待处理的 Lanes
  const nextLanes = getNextLanes(root, NoLanes);
  
  if (nextLanes === NoLanes) {
    // 没有更新需要处理
    return;
  }
  
  // 取消之前的调度
  cancelCallback(root.callbackNode);
  
  // 计算新的优先级
  const newCallbackPriority = getHighestPriorityLane(nextLanes);
  
  // 根据优先级选择调度器
  if (newCallbackPriority === SyncLane) {
    // 同步任务(如 ReactDOM.flushSync)
    root.callbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
  } else {
    // 异步任务(使用 Scheduler)
    const schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);
    
    root.callbackNode = scheduleCallback(
      schedulerPriorityLevel,
      performConcurrentWorkOnRoot.bind(null, root)
    );
  }
}

// 并发模式下的工作入口
function performConcurrentWorkOnRoot(root) {
  const didTimeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  
  // 1. 渲染根节点
  let exitStatus = renderRootConcurrent(root, lanes);
  
  // 2. 检查是否被高优先级任务打断
  if (exitStatus === RootExitStatus.InterruptedByYield) {
    // 时间片用完,保存当前状态,稍后继续
    root.callbackNode = scheduleCallback(
      schedulerPriorityLevel,
      performConcurrentWorkOnRoot.bind(null, root)
    );
    return;
  }
  
  // 3. 渲染完成,进入 Commit 阶段
  const finishedWork = root.finishedWork;
  root.finishedWork = null;
  
  // 4. 提交更新到 DOM
  commitRoot(root);
  
  // 5. 检查是否还有待处理的更新
  ensureRootIsScheduled(root, currentTime);
}

完整调度流程图:
setState/触发更新
获取优先级
SyncLane?
其他优先级
同步执行
渲染
提交
Scheduler 调度
执行
渲染
时间片用完?
重新调度
Schedule
GetNextLanes
Sync
Concurrent
PerformSync
RenderSync
CommitSync
ScheduleCallback
PerformConcurrent
RenderConcurrent
ShouldYield
|是|
让出主线程
|否|
提交
可中断点

高优先级可插入

5.1.2 Lane 优先级计算

源码路径: packages/react-reconciler/src/ReactFiberLane.js

javascript 复制代码
// React 18.3.1 Lane 优先级计算(简化)
function getHighestPriorityLane(lanes: Lanes): Lane {
  return lanes & -lanes; // 取最低位的 1
}

function getLowestPriorityLane(lanes: Lanes): Lane {
  // 获取最高位的 1
  return lanes & ~(1 << (31 - Math.clz32(lanes)));
}

// 合并 Lanes
function mergeLanes(a: Lanes, b: Lanes): Lanes {
  return a | b;
}

// 移除 Lane
function removeLanes(lanes: Lanes, remove: Lanes): Lanes {
  return lanes & ~remove;
}

// 检查是否包含子树 Lane
function includesSomeLane(a: Lanes, b: Lanes): boolean {
  return (a & b) !== NoLanes;
}

// 检查是否是相等或更高优先级
function isSubsetOfLanes(set: Lanes, subset: Lanes): boolean {
  return (set & subset) === subset;
}

// 获取过期时间
function computeExpirationTime(lane: Lane, currentTime: number) {
  switch (lane) {
    case SyncLane:
    case InputContinuousLane:
      return currentTime + 250; // 250ms
    case DefaultLane:
      return currentTime + 5000; // 5s
    case TransitionLane:
      return currentTime + 5000; // 5s
    case IdleLane:
      return NoTimestamp; // 永不过期
    default:
      return currentTime + 5000;
  }
}

5.2 实际应用示例

5.2.1 列表虚拟化 + 时间切片
javascript 复制代码
import { useState, useRef, useEffect, useMemo } from 'react';

/**
 * 虚拟化列表组件
 * 结合时间切片优化大数据渲染
 */
function VirtualizedList({ items = [], itemHeight = 50, containerHeight = 500 }) {
  const [scrollTop, setScrollTop] = useState(0);
  const [visibleData, setVisibleData] = useState([]);
  const containerRef = useRef(null);
  
  // 计算可见范围
  const { startIndex, endIndex, visibleCount } = useMemo(() => {
    const start = Math.floor(scrollTop / itemHeight);
    const visible = Math.ceil(containerHeight / itemHeight);
    const end = Math.min(start + visible + 5, items.length); // +5 缓冲
    return {
      startIndex: Math.max(0, start - 5), // -5 缓冲
      endIndex: end,
      visibleCount: visible
    };
  }, [scrollTop, itemHeight, containerHeight, items.length]);
  
  // 使用时间切片更新可见数据
  useEffect(() => {
    const updateVisibleData = () => {
      const newVisibleData = items.slice(startIndex, endIndex);
      setVisibleData(newVisibleData);
    };
    
    // 使用 requestIdleCallback 进行时间切片
    if ('requestIdleCallback' in window) {
      requestIdleCallback(() => {
        updateVisibleData();
      }, { timeout: 100 });
    } else {
      // 降级到 setTimeout
      setTimeout(updateVisibleData, 0);
    }
  }, [startIndex, endIndex, items]);
  
  const handleScroll = (e) => {
    setScrollTop(e.target.scrollTop);
  };
  
  const totalHeight = items.length * itemHeight;
  const offsetY = startIndex * itemHeight;
  
  return (
    <div
      ref={containerRef}
      onScroll={handleScroll}
      style={{
        height: `${containerHeight}px`,
        overflow: 'auto',
        border: '1px solid #ddd',
        borderRadius: '8px',
        background: '#fafafa'
      }}
    >
      <div style={{ height: `${totalHeight}px`, position: 'relative' }}>
        <div style={{ transform: `translateY(${offsetY}px)` }}>
          {visibleData.map((item, index) => (
            <div
              key={item.id || startIndex + index}
              style={{
                height: `${itemHeight}px`,
                padding: '10px 20px',
                borderBottom: '1px solid #eee',
                background: 'white',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between'
              }}
            >
              <div>
                <strong>{item.name}</strong>
                <p style={{ margin: '5px 0 0 0', fontSize: '14px', color: '#666' }}>
                  {item.description}
                </p>
              </div>
              <div style={{ fontSize: '12px', color: '#999' }}>
                #{startIndex + index}
              </div>
            </div>
          ))}
        </div>
      </div>
      
      {/* 统计信息 */}
      <div style={{
        position: 'fixed',
        bottom: '20px',
        right: '20px',
        background: 'rgba(0,0,0,0.8)',
        color: 'white',
        padding: '10px 20px',
        borderRadius: '8px',
        fontSize: '14px'
      }}>
        <div>总数据: {items.length.toLocaleString()}</div>
        <div>可见: {visibleData.length}</div>
        <div>范围: {startIndex} - {endIndex}</div>
      </div>
    </div>
  );
}

// 使用示例
function VirtualizationDemo() {
  // 生成 100000 条模拟数据
  const [items] = useState(() => {
    return Array.from({ length: 100000 }, (_, i) => ({
      id: i,
      name: `Item ${i + 1}`,
      description: `This is the description for item ${i + 1}`,
      value: Math.random() * 1000
    }));
  });
  
  return (
    <div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
      <h1>虚拟化列表 + 时间切片</h1>
      <p style={{ color: '#666', marginBottom: '20px' }}>
        渲染 100,000 条数据,只渲染可见部分,使用时间切片优化
      </p>
      
      <VirtualizedList
        items={items}
        itemHeight={60}
        containerHeight={600}
      />
      
      <div style={{ marginTop: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
        <h3>💡 优化点:</h3>
        <ul style={{ lineHeight: '1.8' }}>
          <li>✅ 只渲染可见区域的节点(虚拟化)</li>
          <li>✅ 使用 requestIdleCallback 进行时间切片</li>
          <li>✅ 滚动时平滑更新,无卡顿</li>
          <li>✅ 缓冲区提前渲染,避免白屏</li>
        </ul>
      </div>
    </div>
  );
}

export default VirtualizationDemo;
5.2.2 复杂表单 + useTransition
javascript 复制代码
import { useState, useTransition, useCallback } from 'react';

/**
 * 复杂表单组件
 * 使用 useTransition 优化实时验证和计算
 */
function ComplexForm() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    age: '',
    country: '',
    interests: [],
    bio: ''
  });
  
  const [errors, setErrors] = useState({});
  const [suggestions, setSuggestions] = useState([]);
  const [isCalculating, startCalculating] = useTransition();
  const [isValid, setIsValid] = useState(false);
  
  // 模拟昂贵的验证逻辑(如检查用户名是否重复)
  const validateField = useCallback((name, value) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        switch (name) {
          case 'username':
            resolve(
              value.length < 3 
                ? '用户名至少 3 个字符' 
                : value.length > 20 
                  ? '用户名最多 20 个字符' 
                  : null
            );
            break;
          case 'email':
            resolve(
              !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
                ? '邮箱格式不正确'
                : null
            );
            break;
          case 'age':
            resolve(
              value && (value < 18 || value > 100)
                ? '年龄必须在 18-100 之间'
                : null
            );
            break;
          default:
            resolve(null);
        }
      }, 100); // 模拟异步验证
    });
  }, []);
  
  // 模拟获取建议(昂贵的计算)
  const fetchSuggestions = useCallback((text) => {
    if (!text || text.length < 2) return [];
    
    // 模拟大数据集匹配
    const allWords = [
      'apple', 'application', 'approach', 'area',
      'banana', 'balance', 'bank', 'base',
      'camera', 'call', 'can', 'code',
      // ... 更多单词
    ];
    
    return allWords.filter(word => 
      word.toLowerCase().startsWith(text.toLowerCase())
    ).slice(0, 5);
  }, []);
  
  // 处理输入变化
  const handleChange = (e) => {
    const { name, value } = e.target;
    
    // 紧急更新:立即更新表单值
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
    
    // 非紧急更新:验证和建议
    startCalculating(async () => {
      // 验证字段
      const error = await validateField(name, value);
      setErrors(prev => ({
        ...prev,
        [name]: error
      }));
      
      // 获取建议(仅对 bio 字段)
      if (name === 'bio') {
        const words = fetchSuggestions(value.split(' ').pop());
        setSuggestions(words);
      }
    });
  };
  
  // 处理标签选择
  const toggleInterest = (interest) => {
    setFormData(prev => ({
      ...prev,
      interests: prev.interests.includes(interest)
        ? prev.interests.filter(i => i !== interest)
        : [...prev.interests, interest]
    }));
  };
  
  // 提交表单
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('提交表单:', formData);
    alert('表单提交成功!(查看控制台)');
  };
  
  // 检查表单是否有效
  const hasErrors = Object.values(errors).some(error => error !== undefined && error !== null);
  
  return (
    <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
      <h1>复杂表单 + useTransition</h1>
      
      <form onSubmit={handleSubmit} style={{ marginTop: '20px' }}>
        {/* 用户名 */}
        <div style={{ marginBottom: '15px' }}>
          <label style={{ display: 'block', fontWeight: 'bold', marginBottom: '5px' }}>
            用户名:
          </label>
          <input
            type="text"
            name="username"
            value={formData.username}
            onChange={handleChange}
            style={{
              width: '100%',
              padding: '10px',
              border: `2px solid ${errors.username ? 'red' : '#ccc'}`,
              borderRadius: '4px'
            }}
          />
          {errors.username && (
            <div style={{ color: 'red', fontSize: '14px', marginTop: '5px' }}>
              {errors.username}
            </div>
          )}
        </div>
        
        {/* 邮箱 */}
        <div style={{ marginBottom: '15px' }}>
          <label style={{ display: 'block', fontWeight: 'bold', marginBottom: '5px' }}>
            邮箱:
          </label>
          <input
            type="email"
            name="email"
            value={formData.email}
            onChange={handleChange}
            style={{
              width: '100%',
              padding: '10px',
              border: `2px solid ${errors.email ? 'red' : '#ccc'}`,
              borderRadius: '4px'
            }}
          />
          {errors.email && (
            <div style={{ color: 'red', fontSize: '14px', marginTop: '5px' }}>
              {errors.email}
            </div>
          )}
        </div>
        
        {/* 年龄 */}
        <div style={{ marginBottom: '15px' }}>
          <label style={{ display: 'block', fontWeight: 'bold', marginBottom: '5px' }}>
            年龄:
          </label>
          <input
            type="number"
            name="age"
            value={formData.age}
            onChange={handleChange}
            style={{
              width: '100%',
              padding: '10px',
              border: `2px solid ${errors.age ? 'red' : '#ccc'}`,
              borderRadius: '4px'
            }}
          />
          {errors.age && (
            <div style={{ color: 'red', fontSize: '14px', marginTop: '5px' }}>
              {errors.age}
            </div>
          )}
        </div>
        
        {/* 国家 */}
        <div style={{ marginBottom: '15px' }}>
          <label style={{ display: 'block', fontWeight: 'bold', marginBottom: '5px' }}>
            国家:
          </label>
          <select
            name="country"
            value={formData.country}
            onChange={handleChange}
            style={{
              width: '100%',
              padding: '10px',
              border: '2px solid #ccc',
              borderRadius: '4px'
            }}
          >
            <option value="">请选择</option>
            <option value="cn">中国</option>
            <option value="us">美国</option>
            <option value="uk">英国</option>
            <option value="jp">日本</option>
          </select>
        </div>
        
        {/* 兴趣标签 */}
        <div style={{ marginBottom: '15px' }}>
          <label style={{ display: 'block', fontWeight: 'bold', marginBottom: '5px' }}>
            兴趣:
          </label>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px' }}>
            {['编程', '阅读', '音乐', '运动', '旅行', '游戏'].map(interest => (
              <button
                key={interest}
                type="button"
                onClick={() => toggleInterest(interest)}
                style={{
                  padding: '8px 16px',
                  background: formData.interests.includes(interest) ? '#2196F3' : '#e0e0e0',
                  color: formData.interests.includes(interest) ? 'white' : 'black',
                  border: 'none',
                  borderRadius: '20px',
                  cursor: 'pointer'
                }}
              >
                {interest}
              </button>
            ))}
          </div>
        </div>
        
        {/* 简介 */}
        <div style={{ marginBottom: '15px' }}>
          <label style={{ display: 'block', fontWeight: 'bold', marginBottom: '5px' }}>
            简介:
          </label>
          <textarea
            name="bio"
            value={formData.bio}
            onChange={handleChange}
            rows={4}
            style={{
              width: '100%',
              padding: '10px',
              border: '2px solid #ccc',
              borderRadius: '4px',
              resize: 'vertical'
            }}
          />
          {suggestions.length > 0 && (
            <div style={{
              marginTop: '5px',
              padding: '10px',
              background: '#f5f5f5',
              borderRadius: '4px'
            }}>
              <div style={{ fontSize: '12px', color: '#666', marginBottom: '5px' }}>
                建议:
              </div>
              {suggestions.map((word, i) => (
                <span
                  key={i}
                  style={{
                    display: 'inline-block',
                    padding: '4px 8px',
                    margin: '2px',
                    background: 'white',
                    borderRadius: '4px',
                    fontSize: '14px',
                    cursor: 'pointer'
                  }}
                >
                  {word}
                </span>
              ))}
            </div>
          )}
        </div>
        
        {/* 状态指示器 */}
        {isCalculating && (
          <div style={{
            padding: '10px',
            background: '#fff3cd',
            borderRadius: '4px',
            marginBottom: '15px'
          }}>
            ⏳ 正在计算...
          </div>
        )}
        
        {/* 提交按钮 */}
        <button
          type="submit"
          disabled={hasErrors || isCalculating}
          style={{
            width: '100%',
            padding: '12px',
            background: hasErrors ? '#ccc' : '#2196F3',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            fontSize: '16px',
            fontWeight: 'bold',
            cursor: hasErrors ? 'not-allowed' : 'pointer',
            opacity: isCalculating ? 0.6 : 1
          }}
        >
          {isCalculating ? '处理中...' : '提交'}
        </button>
      </form>
      
      {/* 实时预览 */}
      <div style={{ marginTop: '20px', padding: '15px', background: '#f5f5f5', borderRadius: '8px' }}>
        <h3>实时预览</h3>
        <pre style={{ background: 'white', padding: '10px', borderRadius: '4px', overflow: 'auto' }}>
          {JSON.stringify(formData, null, 2)}
        </pre>
      </div>
    </div>
  );
}

export default ComplexForm;

表单优化对比表:

优化技术 未优化 使用 useTransition 性能提升
输入响应 卡顿 流畅 ~80%
验证延迟 阻塞输入 异步非阻塞 ~100%
计算开销 每次输入触发 分批处理 ~60%
用户体验 😞 差 😊 好 显著

6. 性能优化与最佳实践

6.1 何时使用并发特性

搜索/过滤
大数据列表
复杂计算
数据加载
需要优化
场景类型?
startTransition
虚拟化 + Suspense
useDeferredValue
Suspense + Transitions
✅ 降低优先级
✅ 按需渲染
✅ 延迟计算
✅ 优雅降级

决策表:

场景 推荐方案 理由
搜索输入框 startTransition 搜索可延迟,输入必须即时
大列表渲染 虚拟化 + useDeferredValue 只渲染可见部分
数据获取 Suspense + Transitions 优雅的加载状态
复杂计算 Web Worker + Transitions 不阻塞主线程
实时更新 useDeferredValue 延迟不重要的更新

6.2 性能优化清单

✅ DO(推荐做法)
javascript 复制代码
// ✅ 1. 使用 Transitions 处理非紧急更新
const [isPending, startTransition] = useTransition();

function handleSearch(value) {
  setInputValue(value); // 紧急:立即更新输入框
  startTransition(() => {
    setSearchResults(filterData(value)); // 非紧急:延迟搜索
  });
}

// ✅ 2. 使用 useDeferredValue 优化昂贵计算
const deferredQuery = useDeferredValue(query);
const filteredItems = useMemo(() => 
  filterHugeList(deferredQuery),
  [deferredQuery]
);

// ✅ 3. 使用 useMemo 缓存计算结果
const expensiveValue = useMemo(() => {
  return computeExpensiveValue(a, b);
}, [a, b]);

// ✅ 4. 使用 useCallback 缓存函数
const handleClick = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

// ✅ 5. 使用 Suspense 处理异步组件
<Suspense fallback={<Skeleton />}>
  <AsyncComponent />
</Suspense>

// ✅ 6. 虚拟化长列表
<FixedSizeList
  height={600}
  itemCount={10000}
  itemSize={50}
>
  {Row}
</FixedSizeList>
❌ DON'T(避免的做法)
javascript 复制代码
// ❌ 1. 不要在每次渲染时创建新函数/对象
function BadComponent({ items }) {
  return (
    <div>
      {items.map(item => (
        <button onClick={() => console.log(item)}> // 每次创建新函数
          {item.name}
        </button>
      ))}
    </div>
  );
}

// ❌ 2. 不要在渲染中进行昂贵计算
function BadComponent({ data }) {
  return (
    <div>
      {data.map(item => {
        const expensive = heavyCalculation(item); // 每次渲染都计算
        return <div key={item.id}>{expensive}</div>;
      })}
    </div>
  );
}

// ❌ 3. 不要过度使用 useMemo/useCallback
// 如果计算很简单,缓存的开销可能比计算本身还大
const simple = useMemo(() => a + b, [a, b]); // 简单计算不需要缓存

// ❌ 4. 不要忽略依赖项
useEffect(() => {
  fetchData();
}, []); // 如果依赖项变化,effect 不会重新运行

// ❌ 5. 不要在大型列表上使用 map 而不虚拟化
function BadList({ items }) {
  return (
    <div>
      {items.map(item => ( // 10000+ 个节点会卡顿
        <ComplexItem key={item.id} data={item} />
      ))}
    </div>
  );
}

6.3 调试并发模式

javascript 复制代码
import { Profiler } from 'react';

function onRenderCallback(
  id,              // 组件名称
  phase,           // 'mount' 或 'update'
  actualDuration,  // 实际渲染时间
  baseDuration,    // 不使用 memoization 的估计渲染时间
  startTime,       // 开始时间
  commitTime,      // 提交时间
  interactions     // 与此更新相关的交互集合
) {
  console.log({
    id,
    phase,
    actualDuration: `${actualDuration.toFixed(2)}ms`,
    baseDuration: `${baseDuration.toFixed(2)}ms`,
    interactions
  });
}

function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <MyComponent />
    </Profiler>
  );
}

// 使用 React DevTools Profiler
// 1. 打开 React DevTools
// 2. 切换到 Profiler 标签
// 3. 点击录制
// 4. 与应用交互
// 5. 停止录制,查看火焰图

性能分析工具对比:

工具 用途 优势 劣势
React DevTools Profiler 组件渲染分析 可视化、易用 运行时开销
Chrome Performance 整体性能 详细、低级 复杂、难解读
why-did-you-render 检测不必要渲染 自动检测 生产环境开销
React.StrictMode 检测副作用 无侵入性 仅开发模式

7. 总结与展望

7.1 核心要点回顾

本文深入解析了 React 18 并发模式的三大核心技术:

技术 核心思想 解决的问题
Fiber 架构 链表结构替代递归栈 可中断、可恢复的渲染
时间切片 将任务拆分为 5ms 时间片 避免长时间占用主线程
优先级调度 Lane 模型管理任务优先级 高优先级任务优先执行

学习路径图:
React 基础
虚拟 DOM
Reconciliation 算法
Fiber 架构
时间切片
优先级调度
并发模式
Transitions
Suspense
useDeferredValue

7.2 最佳实践总结

🎯 开发建议
  1. 理解优先级:区分紧急和非紧急更新
  2. 合理使用并发特性:不要过度优化
  3. 测量优先于猜测:使用 Profiler 工具
  4. 渐进式采用:从简单的 Transitions 开始
⚠️ 常见陷阱
  1. 滥用 useTransition:所有更新都用 Transition 会适得其反
  2. 忽略性能监控:没有测量就没有优化
  3. 过早优化:先保证正确性,再优化性能
  4. 忽视向后兼容:并发模式是渐进增强的

7.3 未来展望

React 18 的并发模式为未来的 React 发展奠定了基础:

即将到来的特性:

  • React Compiler (Forget):自动优化组件,减少手动 memo 的需求
  • React Server Components (RSC):服务端组件,减少客户端负担
  • Suspense 增强:更好的数据获取体验
  • Actions:表单和异步操作的标准化处理

性能目标:
React 18
并发模式
自动批处理
Transitions
React 19+
React Compiler
Server Components
Actions
更好的 UX
流畅的 60fps 应用

7.4 学习资源

官方资源:

源码阅读路径:

复制代码
packages/react-reconciler/
├── src
│   ├── ReactFiber.js           # Fiber 数据结构
│   ├── ReactFiberLane.js       # 优先级系统
│   ├── ReactFiberWorkLoop.js   # 工作循环
│   ├── ReactFiberReconciler.js # 协调器
│   └── ReactFiberCommit.js     # 提交阶段

packages/scheduler/
├── src
│   ├── forks/Scheduler.js      # 调度器核心
│   └── SchedulerPriorities.js  # 优先级定义

推荐阅读顺序:

  1. 📖 React 官方文档 - 并发模式介绍
  2. 📖 React 工作循环原理
  3. 📖 Fiber 架构深度解析
  4. 📖 Scheduler 源码分析
  5. 💻 实战项目:使用并发特性优化应用

7.5 结语

React 18 的并发模式不仅仅是一次版本升级,更是 React 渲染架构的范式转变 。通过 Fiber 架构、时间切片和优先级调度,React 终于实现了真正的并发渲染

作为开发者,我们需要:

  1. 🧠 理解原理:深入理解 Fiber 和时间切片的工作机制
  2. 🛠️ 掌握工具:熟练使用 Transitions、Suspense 等并发特性
  3. 📊 善于测量:使用 Profiler 等工具分析和优化性能
  4. 🚀 拥抱变化:持续关注 React 的发展,学习新特性

记住:并发模式不是银弹,但它为我们提供了构建更流畅、更响应式 Web 应用的强大工具。


📝 附录

A. 完整示例代码仓库

本文所有示例代码已上传至 GitHub:

  • 仓库地址:[待添加]
  • 在线演示:[待添加]

B. React 版本对照表

React 版本 发布时间 核心特性 并发支持
React 15 2016.04 Stack Reconciler
React 16 2017.09 Fiber 架构
React 17 2020.10 自动批处理
React 18 2022.03 并发模式
React 18.3 2024.04 最新稳定版

C. 术语表

术语 英文 解释
Fiber Fiber React 16+ 的工作单元,包含组件状态和副作用
时间切片 Time Slicing 将长任务拆分为小时间片(~5ms)
并发模式 Concurrent Mode React 可中断和恢复渲染的能力
优先级 Priority 任务的紧急程度,决定执行顺序
Lane Lane 优先级的位掩码表示
Transition Transition 标记非紧急更新的 API
Suspense Suspense 处理异步组件的边界
Reconciliation 协调 React 对比新旧虚拟 DOM 的过程

D. 参考文档

版权声明: 本文由 AI 辅助创作,遵循 CC BY-NC-SA 4.0 协议。欢迎转载,但请注明出处。

相关推荐
万物得其道者成2 小时前
Vue3 使用 Notification 浏览器通知,解决页面关闭后旧通知点击无法跳转问题
前端·vue.js·edge浏览器
ShineWinsu2 小时前
CSS 技术文章
前端·css
天若有情6732 小时前
反向封神!C++ 全局单例不避反用,实现无锁多线程函数独占访问
java·javascript·c++
张风捷特烈2 小时前
状态管理大乱斗#02 | Bloc 源码全面评析
android·前端·flutter
将心ONE2 小时前
pathlib Path函数的使用
java·linux·前端
lingzhilab2 小时前
零知派——ESP32-S3 AI 小智 使用 Preferences NVS 实现Web配网持久化
前端
阿亮爱学代码3 小时前
日期与滚动视图
java·前端·scrollview
欧米欧3 小时前
STRING的底层实现
前端·c++·算法
2301_814809863 小时前
踩坑实战pywebview:用 Python + Web 技术打造轻量级桌面应用
开发语言·前端·python