React16 源码简介
React16 重写了核心算法 reconciler。因为 JavaScript 引擎线程和 UI 渲染线程虽然不是一个线程,但二者是互斥的(因为JS运行结果会影响到UI线程的结果),当浏览器在处理 JavaScript 时,页面就会停止渲染,一旦 JavaScript 执行占用的时间过长,留给 UI 渲染的时间就会缩短,从而造成页面每秒渲染的帧数过低,导致页面产生明显的卡顿感。
React 源码中 package 下的几个关键模块:
- events:事件系统。
- react:定义节点及其表现行为的包,代码量很少,JSX 依赖该模块。
- react-dom:与更新和渲染相关,取决于平台(react-dom 和 react-native 中可能不同),依赖 react-reconciler、schedule。
ReactDom.render
前言
ReactDom.render(
element: React$Element,
container: DOMContainer,
callback: ?Function,
)
ReactDom.render 的第一个参数为 ReactElement,那么 element、component 和 instance 的关系是什么呢?
element:一个可以描述 DOM 节点或者 component 实例的对象,可以通过 React.createElement() 或者 JSX 语法创建。
component:可以通过 class 或者 function 声明。
instance:只有类组件才有实例,React 会自动创建实例。
component
class App extends React.Component {
render() {
hello!
}
}
element & instance
dom element
hello!
源码阅读
1.入口
// packages/react-dom/src/client/ReactDOM.js
render(
element: React$Element, // 要渲染的元素
container: DOMContainer, // 根节点
callback: ?Function, // 渲染完执行的回调
) {
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
2.创建 root,然后调用 root.render,最终返回 DOM 节点信息
// packages/react-dom/src/client/ReactDOM.js
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component, // null
children: ReactNodeList, //element
container: DOMContainer, //container
forceHydrate: boolean, //false
callback: ?Function, //callback
) {
let root: Root = (container._reactRootContainer: any);
// 首次渲染,root 不存在
if (!root) {
// Initial mount
// 创建 root
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate, // 在 render 中是 false,hydrate 中为 true,决定是否复用 DOM 节点
);
if (typeof callback === 'function') {
// 执行回调
}
// Initial mount should not be batched.
// 不使用 batchedUpdates,因为首次渲染需要尽快完成
unbatchedUpdates(() => {
if (parentComponent != null) {
// ...
} else {
// render 中 parentComponent = null
root.render(children, callback);
}
});
} else {
// root 存在的情况
}
return getPublicRootInstance(root._internalRoot);
}
2.1 创建 root
function legacyCreateRootFromDOMContainer(
container: DOMContainer,
forceHydrate: boolean,
): Root {
// ...
// Legacy roots are not async by default.
const isConcurrent = false;
return new ReactRoot(container, isConcurrent, shouldHydrate);
}
function ReactRoot(
container: DOMContainer,
isConcurrent: boolean,
hydrate: boolean,
) {
const root = createContainer(container, isConcurrent, hydrate);
this._internalRoot = root;
}
function createContainer(
containerInfo: Container,
isConcurrent: boolean,
hydrate: boolean,
): OpaqueRoot {
// 最终返回 FiberRoot 对象
return createFiberRoot(containerInfo, isConcurrent, hydrate);
}
2.2 调用 root.render
ReactRoot.prototype.render = function(
children: ReactNodeList,
callback: ?() => mixed,
): Work {
const root = this._internalRoot;
const work = new ReactWork();
callback = callback === undefined ? null : callback;
if (callback !== null) {
work.then(callback);
}
updateContainer(children, root, null, work._onCommit);
return work;
};
function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component,
callback: ?Function,
): ExpirationTime {
const current = container.current;
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, current);
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
callback,
);
}
function updateContainerAtExpirationTime(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component,
expirationTime: ExpirationTime,
callback: ?Function,
) {
const current = container.current;
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
return scheduleRootUpdate(current, element, expirationTime, callback);
}
function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
callback: ?Function,
) {
// 创建更新
const update = createUpdate(expirationTime);
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element};
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}
// 将更新放入队列
enqueueUpdate(current, update);
// 开始调度。React16+提出了任务优先级的概念。
scheduleWork(current, expirationTime);
return expirationTime;
}
function createUpdate(expirationTime: ExpirationTime): Update<*> {
return {
expirationTime: expirationTime,
tag: UpdateState,
payload: null,
callback: null,
next: null,
nextEffect: null,
};
}
2.3 返回真实 DOM
HostComponent:抽象节点,是 ClassComponent 的组成部分。具体的实现取决于 React 运行的平台。在浏览器环境下就代表 DOM 节点。
Fiber:Fiber 是一个对象,表征 reconciliation 阶段所能拆分的最小工作单元,和 React Instance 一一对应。并通过 stateNode 属性管理 Instance 的 local state。
// packages/react-reconciler/src/ReactFiberReconciler.js
function getPublicRootInstance(
container: OpaqueRoot,
): React$Component | PublicInstance | null {
// container.current 为 container 对应的 Fiber
const containerFiber = container.current;
if (!containerFiber.child) {
return null;
}
// 判断 Fiber 子节点的类型
switch (containerFiber.child.tag) {
// 真实 DOM 如 div, span 等
case HostComponent:
return getPublicInstance(containerFiber.child.stateNode);
default:
// stateNode 是跟当前 Fiber 相关联的本地状态(比如浏览器环境就是真实的 DOM 节点)
return containerFiber.child.stateNode;
}
}
function getPublicInstance(instance: Instance): * {
return instance;
}
参考文献
https://reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html