React源码解析与优化
重要通知
研究与解析React源码的动机,是基于React源码的实现机制,更好地在基础架构与业务具体实现中将代码质量提升到更好的水平,同时能够兼顾更好状态的性能提升,可以充分运用React的核心优势,降低系统设计的冗余与业务实现的臃肿,实现以React生态技术栈为中心的最佳架构设计与业务功能实现。本文档的所有解析与优化,都是基于version: "18.2.0"版本。
基本概况
- 官网:https://reactjs.org/
- 中文文档:https://react.docschina.org/
- GitHub:https://github.com/facebook/react/
- 已发行版本: https://github.com/facebook/react/releases
React与微线程
链表、事件循环
环状流的概念
页面模板渲染工作流程
页面模板代码示例
<div id="app"></div>
<script type="text/javascript">
class App extends React.Component {
constructor(props) {
super(props);
this.state = { title: "React源码解析与优化" };
}
render() {
return (
<section>{this.state.title}</section>
);
}
};
const htmlFragment = <App />;
const dom = ReactDOM.createRoot(
document.querySelector('#app'),
).render(htmlFragment);
</script>
Fiber数据结构
function FiberNode(tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode) {
// Instance 实例属性
this.tag = tag; // 标记不同组件类型,如函数组件、类组件、文本、原生组件...
this.key = key; // react 元素上的 key 就是 jsx 上写的那个 key ,也就是最终 ReactElement 上的
this.elementType = null; // createElement的第一个参数,ReactElement 上的 type
this.type = null; // 表示fiber的真实类型 ,elementType 基本一样,在使用了懒加载之类的功能时可能会不一样
this.stateNode = null; // 实例对象,比如 class 组件 new 完后就挂载在这个属性上面,如果是RootFiber,那么它上面挂的是 FiberRoot,如果是原生节点就是 dom 对象
// fiber
this.return = null; // 父节点,指向上一个 fiber
this.child = null; // 子节点,指向自身下面的第一个 fiber
this.sibling = null; // 兄弟组件, 指向一个兄弟节点
this.index = 0; // 一般如果没有兄弟节点的话是0 当某个父节点下的子节点是数组类型的时候会给每个子节点一个 index,index 和 key 要一起做 diff
this.ref = null; // reactElement 上的 ref 属性
this.pendingProps = pendingProps; // 新的 props
this.memoizedProps = null; // 旧的 props
this.updateQueue = null; // fiber 上的更新队列执行一次 setState 就会往这个属性上挂一个新的更新, 每条更新最终会形成一个链表结构,最后做批量更新
this.memoizedState = null; // 对应 memoizedProps,上次渲染的 state,相当于当前的 state,理解成 prev 和 next 的关系
this.mode = mode; // 表示当前组件下的子组件的渲染方式
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null; // current 树和 workInprogress 树之间的相互引用
}
Fiber双缓存
在经过reconcile(diff)形成了新的workInProgress Fiber,然后将workInProgress Fiber切换成current Fiber应用到真实dom中,存在双Fiber的好处是在内存中形成视图的描述,在最后应用到dom中,减少了对dom的操作。
var createFiber = function (tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode) {
return new FiberNode(tag, pendingProps, key, mode);
};
function createWorkInProgress(current: Fiber, pendingProps, expirationTime): Fiber {}
function resetWorkInProgress(workInProgress: Fiber, renderExpirationTime) {}
调度器-Scheduler
调度任务的优先级,高优任务优先进入Reconciler。
协调器-Reconciler
异步可中断更新,负责找出变化的组件,使用时间片
渲染器-Render
负责将变化的组件渲染到页面上。
ReactDOM
state更新机制
- 触发更新的方式
- ReactDOM.render()
- this.setState
- this.forceUpdate:设置update.tag为ForceUpdate,
- useState
- useReducer
Component.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState');
}
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
- state更新流程
- React 组件状态更新,向 Scheduler 中存入一个任务,该任务为 React 更新算法。[Scheduler 需要暴露 pushTask() 方法,React 通过该方法存入任务。]
- Scheduler 调度该任务,执行 React 更新算法。[Scheduler 需要暴露 scheduleTask() 方法,用于调度任务。]
- React 在调和阶段更新一个 Fiber 之后,会询问 Scheduler 是否需要暂停。如果不需要暂停,则重复步骤 3,继续更新下一个 Fiber。[Scheduler 需要暴露 shouldYield() 方法,React 通过该方法决定是否需要暂停执行该任务。]
- 如果 Scheduler 表示需要暂停,则 React 将返回一个函数,该函数用于告诉 Scheduler 任务还没有完成。Scheduler 将在未来某时刻调度该任务。[Scheduler 判断任务执行后的返回值是否是一个函数,如果是则说明任务未完成,将来还需要调度它。]

enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
const update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
update.callback = callback;
}
enqueueUpdate(fiber, update, lane);
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if (root !== null) {
entangleTransitions(root, fiber, lane);
}
if (__DEV__) {
if (enableDebugTracing) {
if (fiber.mode & DebugTracingMode) {
const name = getComponentNameFromFiber(fiber) || 'Unknown';
logStateUpdateScheduled(name, lane, payload);
}
}
}
if (enableSchedulingProfiler) {
markStateUpdateScheduled(fiber, lane);
}
}
模板字符串工作流程
响应式系统工作流程
Diff算法
JSX实现原理
JSX是React.createElement(component, props, ...children) 函数的语法糖。
<div className="sidebar" />
// Babel编译成
React.createElement(
'div',
{className: 'sidebar'},
null
)
<div>Hello world!</div>
// Babel编译成
React.createElement(
"div",
null,
"Hello world!"
)
Event事件系统
批量更新 batchedUpdates
Time Slicing 时间分片
把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。结合了requestAnimationFrame和MessageChannel实现了类似requestIdleCallback的功能,requestIdleCallback并不稳定,所以React抛弃了它。
任务优先级
React Fiber 把更新过程碎片化,每执行完一段更新过程,就把控制权交还给 React 负责任务协调的模块,看看有没有其他紧急任务要做,如果没有就继续去更新,如果有紧急任务,那就去做紧急任务。