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 模块 来实现时间切片,它基于 MessageChannel 和 requestIdleCallback 思想实现。

源码位置: 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 协议。欢迎转载,但请注明出处。

相关推荐
程序员Sunday44 分钟前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
Setsuna_F_Seiei1 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
zhangzeyuaaa2 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙2 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯2 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
思无邪663 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
一个风轻云淡3 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u0111026754 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式
福兮说4 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
libokaifa5 小时前
把语音模型塞进手机:sherpa-onnx 在 Android 上的 ASR / TTS
前端