React 16 - Fiber 架构革命
⚡ 深入理解 React 16 的 Fiber 架构重写、可中断渲染和现代 React 特性
📅 版本里程碑
- 发布时间: 2017年9月
- 核心突破: Fiber 架构完全重写
- 关键特性: 可中断渲染、时间切片、优先级调度
- 重大更新: Error Boundaries、Fragments、Portals
- 历史意义: React 现代化的起点,奠定并发基础
🚀 Fiber 架构革命
设计动机
React 16 的 Fiber 重写解决了 React 15 的根本性问题:
🚨 React 15 的痛点
javascript
// React 15 的问题:长任务阻塞
function heavyRender() {
// 🚨 这个渲染过程无法中断
return (
<div>
{largeDataSet.map(item => (
<ComplexComponent key={item.id} data={item} />
))}
</div>
);
}
// 结果:主线程被阻塞 100ms+,用户界面卡顿✨ Fiber 的解决方案
javascript
// React 16+ 的改进:可中断渲染
function workLoop(deadline) {
while (nextUnitOfWork && deadline.timeRemaining() > 1) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
if (nextUnitOfWork) {
// 时间不够了,让出控制权给浏览器
requestIdleCallback(workLoop);
} else {
// 工作完成,提交更新
commitRoot();
}
}核心创新
1. 可中断渲染
Fiber 将渲染工作分解为小的工作单元:
typescript
interface Fiber {
// 组件信息
type: string | Function; // 组件类型
key: string | null; // React key
elementType: any; // 元素类型
// 实例和状态
stateNode: any; // 对应的 DOM 节点或组件实例
memoizedProps: any; // 上次渲染的 props
memoizedState: any; // 上次渲染的 state
// Fiber 树结构
return: Fiber | null; // 父 Fiber
child: Fiber | null; // 第一个子 Fiber
sibling: Fiber | null; // 下一个兄弟 Fiber
index: number; // 在兄弟节点中的索引
// 工作相关
pendingProps: any; // 新的 props
updateQueue: UpdateQueue | null; // 更新队列
// 调度相关
lanes: Lanes; // 优先级车道
childLanes: Lanes; // 子树的优先级
// 副作用
flags: Flags; // 副作用标记
subtreeFlags: Flags; // 子树副作用
deletions: Array<Fiber> | null; // 需要删除的子节点
// 双缓存
alternate: Fiber | null; // 对应的另一棵树的 Fiber
}2. 双缓存机制
typescript
// 双缓存 Fiber 树
interface FiberRoot {
current: Fiber; // 当前显示的 Fiber 树
finishedWork: Fiber; // 完成工作的 Fiber 树
// 工作进度
pendingLanes: Lanes; // 待处理的优先级
finishedLanes: Lanes; // 已完成的优先级
}
// 渲染过程
function renderWithFiber() {
// 1. 创建 workInProgress 树
const workInProgress = createWorkInProgress(current);
// 2. 在 workInProgress 树上进行更新
workLoopSync();
// 3. 完成后交换两棵树
root.current = workInProgress;
}3. 时间切片技术
javascript
// React 16 的时间切片实现
function workLoopConcurrent() {
// 在有剩余时间时继续工作
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
function shouldYield() {
const currentTime = getCurrentTime();
// 如果超过了时间片,让出控制权
if (currentTime >= deadline) {
return true;
}
// 检查是否有更高优先级的工作
if (hasHigherPriorityWork()) {
return true;
}
return false;
}
// 浏览器空闲时继续工作
function scheduleWork() {
if (isYieldy) {
requestIdleCallback(performWork);
} else {
requestAnimationFrame(performWork);
}
}🎭 工作循环机制
Render 阶段(可中断)
javascript
// Fiber 的 Render 阶段
function renderRootSync(root, lanes) {
// 准备工作
prepareFreshStack(root, lanes);
do {
try {
// 工作循环 - 可以被中断
workLoopSync();
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
// 完成工作
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
}
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
// 开始工作
let next = beginWork(current, unitOfWork, renderLanes);
if (next === null) {
// 完成工作
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
}Commit 阶段(不可中断)
javascript
// Fiber 的 Commit 阶段
function commitRoot(root) {
const finishedWork = root.finishedWork;
// 三个子阶段,都是同步执行
// 1. Before Mutation 阶段
commitBeforeMutationEffects(root, finishedWork);
// 2. Mutation 阶段 - 执行 DOM 操作
commitMutationEffects(root, finishedWork);
// 3. Layout 阶段 - 执行副作用
commitLayoutEffects(finishedWork, root);
// 切换 current 指针
root.current = finishedWork;
// 调度后续工作
scheduleCallback(flushPassiveEffects);
}🎯 优先级调度系统
车道模型(Lanes)
typescript
// React 16+ 的优先级系统
type Lanes = number;
type Lane = number;
// 优先级定义(从高到低)
const SyncLane: Lane = 0b0000000000000000000000000000001;
const InputContinuousLane: Lane = 0b0000000000000000000000000000010;
const DefaultLane: Lane = 0b0000000000000000000000000010000;
const TransitionLane1: Lane = 0b0000000000000000000000001000000;
const RetryLane1: Lane = 0b0000000000000000000100000000000;
const IdleLane: Lane = 0b0100000000000000000000000000000;
const OffscreenLane: Lane = 0b1000000000000000000000000000000;
// 调度逻辑
function scheduleUpdateOnFiber(fiber: Fiber, lane: Lane) {
// 标记 Fiber 需要更新
markUpdateLaneFromFiberToRoot(fiber, lane);
if (lane === SyncLane) {
// 同步更新 - 立即执行
performSyncWorkOnRoot(root);
} else {
// 异步更新 - 调度执行
scheduleCallback(
ImmediatePriority,
() => performConcurrentWorkOnRoot(root)
);
}
}
// 优先级比较
function getHighestPriorityLane(lanes: Lanes): Lane {
return lanes & -lanes; // 获取最低位的 1
}
function includesNonIdleWork(lanes: Lanes): boolean {
return (lanes & NonIdleLanes) !== NoLanes;
}调度器集成
javascript
// React 16 与 Scheduler 的集成
import {
scheduleCallback,
shouldYield,
ImmediatePriority,
UserBlockingPriority,
NormalPriority,
LowPriority,
IdlePriority
} from 'scheduler';
function ensureRootIsScheduled(root) {
const nextLanes = getNextLanes(root);
if (nextLanes === NoLanes) {
return; // 没有工作需要做
}
const newCallbackPriority = getHighestPriorityLane(nextLanes);
// 根据优先级选择调度策略
let schedulerPriorityLevel;
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediatePriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdlePriority;
break;
default:
schedulerPriorityLevel = NormalPriority;
}
// 调度工作
const newCallback = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
root.callbackNode = newCallback;
}🎣 Hooks 系统引入
Hook 的内部实现
javascript
// React 16.8 引入的 Hook 系统
let currentlyRenderingFiber = null;
let currentHook = null;
let workInProgressHook = null;
function useState(initialState) {
return useReducer(basicStateReducer, initialState);
}
function useReducer(reducer, initialArg, init) {
const fiber = currentlyRenderingFiber;
const hook = updateWorkInProgressHook();
// 处理更新队列
const queue = hook.queue;
if (queue !== null) {
// 处理更新
const dispatch = queue.dispatch;
const pending = queue.pending;
if (pending !== null) {
queue.pending = null;
// 计算新状态
const newState = processUpdateQueue(hook.memoizedState, pending, reducer);
hook.memoizedState = newState;
}
}
const dispatch = dispatchAction.bind(null, fiber, queue);
queue.dispatch = dispatch;
return [hook.memoizedState, dispatch];
}
function useEffect(create, deps) {
const fiber = currentlyRenderingFiber;
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
if (hook !== null) {
const prevEffect = hook.memoizedState;
destroy = prevEffect.destroy;
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
// 浅比较依赖数组
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 依赖没变,跳过副作用
hook.memoizedState = pushEffect(NoHookEffect, create, destroy, nextDeps);
return;
}
}
}
// 标记需要执行副作用
currentlyRenderingFiber.flags |= UpdateEffect;
hook.memoizedState = pushEffect(
HookHasEffect | hookEffectTag,
create,
destroy,
nextDeps
);
}Hook 链表结构
typescript
interface Hook {
memoizedState: any; // Hook 的状态值
baseState: any; // 基础状态
baseQueue: Update | null; // 基础更新队列
queue: UpdateQueue | null; // 当前更新队列
next: Hook | null; // 下一个 Hook
}
interface Effect {
tag: HookFlags; // 副作用标记
create: () => (() => void) | void; // 副作用函数
destroy: (() => void) | void; // 清理函数
deps: Array<mixed> | null; // 依赖数组
next: Effect; // 形成环形链表
}
// Hook 链表示例
const hookChain = {
hook1: { // useState
memoizedState: 'hello',
next: hook2
},
hook2: { // useEffect
memoizedState: {
tag: HookHasEffect,
create: () => { console.log('effect'); },
destroy: undefined,
deps: ['hello']
},
next: hook3
},
hook3: { // useMemo
memoizedState: computedValue,
next: null
}
};⚙️ 协调算法演进
从递归到循环
React 15 递归方式
javascript
// 栈协调器的递归实现
function reconcileChildren(instance, nextChildren) {
// 🚨 深度优先递归,无法中断
nextChildren.forEach(child => {
if (child.type) {
const childInstance = instantiateComponent(child);
reconcileChildren(childInstance, child.props.children);
}
});
}React 16 循环方式
javascript
// Fiber 的循环实现
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
// 开始工作:处理当前 Fiber
let next = beginWork(current, unitOfWork, renderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// 没有子节点,完成当前单元
completeUnitOfWork(unitOfWork);
} else {
// 有子节点,继续处理子节点
workInProgress = next;
}
}
function completeUnitOfWork(unitOfWork) {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
// 完成当前 Fiber 的工作
let next = completeWork(current, completedWork, renderLanes);
if (next !== null) {
workInProgress = next;
return;
}
// 处理兄弟节点
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
workInProgress = siblingFiber;
return;
}
// 返回父节点
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
}树遍历策略
javascript
// Fiber 树的遍历顺序
function traverseFiberTree(fiber) {
let node = fiber;
while (true) {
// 1. 处理当前节点
performWork(node);
// 2. 如果有子节点,进入子节点
if (node.child) {
node = node.child;
continue;
}
// 3. 如果没有子节点,完成当前节点
completeNode(node);
// 4. 处理兄弟节点
if (node.sibling) {
node = node.sibling;
continue;
}
// 5. 返回父节点
while (node.return && !node.return.sibling) {
node = node.return;
completeNode(node);
}
if (!node.return) {
break; // 到达根节点
}
node = node.return.sibling;
}
}
/*
遍历顺序示例:
App
/ \
A B
/ \ /
C D E
执行顺序:App → A → C → D → A(完成) → B → E → B(完成) → App(完成)
*/🚦 优先级与调度
事件优先级映射
javascript
// 不同事件对应的优先级
function getEventPriority(domEventName) {
switch (domEventName) {
// 离散事件 - 最高优先级
case 'click':
case 'keydown':
case 'keyup':
case 'input':
return DiscreteEventPriority;
// 连续事件 - 高优先级
case 'drag':
case 'dragover':
case 'mousemove':
case 'scroll':
return ContinuousEventPriority;
// 默认事件 - 普通优先级
default:
return DefaultEventPriority;
}
}
// 根据事件优先级决定更新方式
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
if (lane === SyncLane) {
if (
(executionContext & LegacyUnbatchedContext) !== NoContext &&
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
// 同步更新
performSyncWorkOnRoot(root);
} else {
// 批量更新
ensureRootIsScheduled(root, eventTime);
}
} else {
// 异步更新
ensureRootIsScheduled(root, eventTime);
}
}饥饿问题解决
javascript
// 防止低优先级任务饥饿
function markStarvedLanesAsExpired(root, currentTime) {
const pendingLanes = root.pendingLanes;
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
let lanes = pendingLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
const expirationTime = root.expirationTimes[index];
if (expirationTime === NoTimestamp) {
// 设置过期时间
root.expirationTimes[index] = computeExpirationTime(lane, currentTime);
} else if (expirationTime <= currentTime) {
// 已过期,提升为同步优先级
root.expiredLanes |= lane;
}
lanes &= ~lane;
}
}🆕 React 16 新特性
1. Error Boundaries
javascript
// React 16 引入的错误边界
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
console.error('ErrorBoundary caught an error:', error, errorInfo);
this.setState({
error: error,
errorInfo: errorInfo
});
// 上报错误到监控系统
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return (
<div>
<h2>Something went wrong.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
// 使用错误边界
function App() {
return (
<ErrorBoundary>
<Header />
<MainContent />
<Sidebar />
</ErrorBoundary>
);
}2. Fragments
javascript
// React 16 支持 Fragment
class FragmentExample extends React.Component {
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
}
// 简写语法
function ShortFragment() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
// 带 key 的 Fragment
function FragmentWithKey({ items }) {
return (
<dl>
{items.map(item => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
))}
</dl>
);
}3. Portals
javascript
// React 16 的 Portal 功能
import ReactDOM from 'react-dom';
class Modal extends React.Component {
constructor(props) {
super(props);
this.modalRoot = document.getElementById('modal-root');
this.el = document.createElement('div');
}
componentDidMount() {
this.modalRoot.appendChild(this.el);
}
componentWillUnmount() {
this.modalRoot.removeChild(this.el);
}
render() {
// Portal 可以将子组件渲染到不同的 DOM 节点
return ReactDOM.createPortal(
this.props.children,
this.el
);
}
}
// 使用 Portal
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<h1>App Content</h1>
<button onClick={() => setShowModal(true)}>
Show Modal
</button>
{showModal && (
<Modal>
<div className="modal">
<h2>Modal Content</h2>
<button onClick={() => setShowModal(false)}>
Close
</button>
</div>
</Modal>
)}
</div>
);
}4. 改进的服务端渲染
javascript
// React 16 改进的 SSR
import { renderToString, renderToNodeStream } from 'react-dom/server';
// 字符串渲染(同步)
const html = renderToString(<App />);
// 流式渲染(异步)
const stream = renderToNodeStream(<App />);
stream.pipe(response);
// 客户端 hydration
import { hydrate } from 'react-dom';
hydrate(<App />, document.getElementById('root'));📊 性能提升对比
渲染性能对比
| 指标 | React 15 | React 16 | 提升幅度 |
|---|---|---|---|
| 大列表渲染 | 阻塞 50ms+ | 分片处理 | 🚀 流畅度提升 80% |
| 复杂更新 | 一次性处理 | 可中断恢复 | ⚡ 响应性提升 60% |
| 内存使用 | 峰值较高 | 更平稳 | 📉 峰值降低 30% |
| 首屏时间 | 同步渲染 | 渐进渲染 | 🎯 感知性能提升 40% |
实际测试数据
javascript
// 性能测试工具
function measureRenderPerformance(Component, props) {
const measurements = [];
for (let i = 0; i < 10; i++) {
const start = performance.now();
ReactDOM.render(<Component {...props} />, container);
const end = performance.now();
measurements.push(end - start);
}
return {
average: measurements.reduce((a, b) => a + b) / measurements.length,
min: Math.min(...measurements),
max: Math.max(...measurements),
p95: measurements.sort()[Math.floor(measurements.length * 0.95)]
};
}
// 测试结果示例
const performanceResults = {
react15: {
smallApp: { average: 2.1, max: 4.5, p95: 3.8 },
mediumApp: { average: 12.3, max: 28.7, p95: 25.1 },
largeApp: { average: 45.6, max: 89.3, p95: 78.2 }
},
react16: {
smallApp: { average: 1.8, max: 3.2, p95: 2.9 },
mediumApp: { average: 8.7, max: 16.4, p95: 14.2 },
largeApp: { average: 18.9, max: 32.1, p95: 28.5 }
}
};🔧 开发体验改进
1. 更好的错误提示
javascript
// React 16 的友好错误信息
function BuggyComponent() {
throw new Error('I crashed!');
}
// React 15 错误信息:
// "Uncaught Error: I crashed!"
// React 16 错误信息:
// "Error: I crashed!
// in BuggyComponent (at App.js:15)
// in ErrorBoundary (at App.js:10)
// in div (at App.js:8)
// in App (at index.js:6)"2. 开发工具增强
javascript
// React 16 的开发工具支持
if (__DEV__) {
// 组件栈追踪
ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
// 性能分析
if (enableProfilerTimer) {
startProfilerTimer(fiber);
}
// 严格模式检查
if (fiber.mode & StrictMode) {
runStrictModeChecks(fiber);
}
}🎯 迁移指南
从 React 15 升级
1. 生命周期更新
javascript
// React 15
class OldComponent extends React.Component {
componentWillMount() {
// 🚨 将被废弃
this.setupSubscription();
}
componentWillReceiveProps(nextProps) {
// 🚨 将被废弃
if (nextProps.userId !== this.props.userId) {
this.loadUserData(nextProps.userId);
}
}
componentWillUpdate(nextProps, nextState) {
// 🚨 将被废弃
this.prepareUpdate(nextProps, nextState);
}
}
// React 16+
class NewComponent extends React.Component {
constructor(props) {
super(props);
this.setupSubscription();
}
static getDerivedStateFromProps(props, state) {
if (props.userId !== state.prevUserId) {
return {
prevUserId: props.userId,
userData: null // 触发重新加载
};
}
return null;
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.userId !== this.props.userId) {
this.loadUserData(this.props.userId);
}
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 在 DOM 更新前获取信息
return this.listRef.current.scrollTop;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot !== null) {
this.listRef.current.scrollTop = snapshot;
}
}
}2. 错误处理升级
javascript
// React 15 - 没有错误边界
class OldApp extends React.Component {
render() {
return (
<div>
<UnstableComponent /> {/* 错误会导致整个应用崩溃 */}
</div>
);
}
}
// React 16 - 添加错误边界
class NewApp extends React.Component {
render() {
return (
<div>
<ErrorBoundary>
<UnstableComponent /> {/* 错误被捕获,应用继续运行 */}
</ErrorBoundary>
</div>
);
}
}🔮 架构影响
为现代特性奠定基础
Fiber 架构为后续特性提供了基础:
React 16.6 - Suspense
javascript
// Suspense 需要 Fiber 的可中断特性
function ProfilePage({ userId }) {
return (
<Suspense fallback={<Loading />}>
<ProfileDetails userId={userId} />
<ProfileTimeline userId={userId} />
</Suspense>
);
}
// Fiber 可以暂停组件树的渲染,等待异步数据React 16.8 - Hooks
javascript
// Hooks 需要 Fiber 的状态管理
function HookComponent() {
const [state, setState] = useState(0); // 存储在 Fiber.memoizedState
useEffect(() => {
// 副作用存储在 Fiber 的 effect 链表中
}, [state]);
return <div>{state}</div>;
}React 18 - 并发特性
javascript
// 并发特性基于 Fiber 的调度能力
function ConcurrentApp() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<Suspense fallback={<Loading />}>
<SearchResults query={deferredQuery} />
</Suspense>
</div>
);
}🎓 学习价值
理解现代 React
学习 Fiber 架构有助于理解:
- 时间切片 - 为什么 React 能保持流畅
- 优先级调度 - 为什么用户交互总是优先响应
- 并发模式 - React 18 并发特性的基础
- Hook 原理 - Hook 如何存储和管理状态
性能优化指导
理解 Fiber 有助于:
- 识别性能瓶颈 - 知道什么操作会触发重渲染
- 优化策略选择 - 选择合适的优化方案
- 调试技巧 - 使用 React DevTools 分析性能
- 架构设计 - 设计性能友好的组件结构
🔗 相关资源
🎯 下一步
掌握 React 16 Fiber 架构后,建议:
- 架构对比分析 - 深度对比栈协调器与 Fiber
- React 17 渐进升级 - 了解平滑过渡策略
- React 18 并发特性 - 探索最新的并发能力
- 实践项目 - 动手体验 Fiber 优势
Fiber 架构是理解现代 React 的关键,它不仅解决了性能问题,更为 React 的未来发展奠定了坚实基础!🚀