共计 32101 个字符,预计需要花费 81 分钟才能阅读完成。
首先 我这里 有一份配置好的 react 源码配置 地址如下
仓库 地址
git 地址:git@github.com:544076724/react16-source.git
下载下来间接 yarn start 启动就能够了
前言:react 我的项目是基于 Monorepo 构造开发的,后续文章中所呈现的地址都是咱们工程的
src/react/packages/ 这一级来找的具体包
接下来咱们简略做下剖析
咱们这篇外面临时不波及太多的优先级工作,react 源码里的优先级工作 是通过计算一个过期工夫来计算的 他们本人实现的一套算法 这里临时不介绍这个算法 大家晓得这个优先级算法是 requestIdleCallback 的欠缺版 就能够
咱们晓得后面说了 fiber 构造 而 fiber 的构造是由 vnode 转换过去的,JSX 被 Babel 编译为 React.createElement 办法的调用,createElement 办法在调用后返回的就是 ReactElement,就是 virtualDOM。
createElement
文件地位:packages/react/src/ReactElement.js
/**
* 创立 React Element
* type 元素类型
* config 配置属性
* children 子元素
* 1. 拆散 props 属性和非凡属性
* 2. 将子元素挂载到 props.children 中
* 3. 为 props 属性赋默认值 (defaultProps)
* 4. 创立并返回 ReactElement
*/
export function createElement(type, config, children) {
/**
* propName -> 属性名称
* 用于前面的 for 循环
*/
let propName;
/**
* 存储 React Element 中的一般元素属性 即不蕴含 key ref self source
*/
const props = {};
/**
* 待提取属性
* React 外部为了实现某些性能而存在的属性
*/
let key = null;
let ref = null;
let self = null;
let source = null;
// 如果 config 不为 null
if (config != null) {
// 如果 config 对象中有非法的 ref 属性
if (hasValidRef(config)) {
// 将 config.ref 属性提取到 ref 变量中
ref = config.ref;
// 在开发环境中
if (__DEV__) {
// 如果 ref 属性的值被设置成了字符串模式就报一个提醒
// 阐明此用法在未来的版本中会被删除
warnIfStringRefCannotBeAutoConverted(config);
}
}
// 如果在 config 对象中领有非法的 key 属性
if (hasValidKey(config)) {
// 将 config.key 属性中的值提取到 key 变量中
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// 遍历 config 对象
for (propName in config) {
// 如果以后遍历到的属性是对象本身属性
// 并且在 RESERVED_PROPS 对象中不存在该属性
if (hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {// 将满足条件的属性增加到 props 对象中 (一般属性)
props[propName] = config[propName];
}
}
}
/**
* 将第三个及之后的参数挂载到 props.children 属性中
* 如果子元素是多个 props.children 是数组
* 如果子元素是一个 props.children 是对象
*/
// 因为从第三个参数开始及当前都示意子元素
// 所以减去前两个参数的后果就是子元素的数量
const childrenLength = arguments.length - 2;
// 如果子元素的数量是 1
if (childrenLength === 1) {
// 间接将子元素挂载到到 props.children 属性上
// 此时 children 是对象类型
props.children = children;
// 如果子元素的数量大于 1
} else if (childrenLength > 1) {
// 创立数组, 数组中元素的数量等于子元素的数量
const childArray = Array(childrenLength);
// 开启循环 循环次匹配子元素的数量
for (let i = 0; i < childrenLength; i++) {
// 将子元素增加到 childArray 数组中
// i + 2 的起因是实参汇合的前两个参数不是子元素
childArray[i] = arguments[i + 2];
}
// 如果是开发环境
if (__DEV__) {
// 如果 Object 对象中存在 freeze 办法
if (Object.freeze) {
// 调用 freeze 办法 解冻 childArray 数组
// 避免 React 外围对象被批改 解冻对象进步性能
Object.freeze(childArray);
}
}
// 将子元素数组挂载到 props.children 属性中
props.children = childArray;
}
/**
* 如果以后解决是组件
* 看组件身上是否有 defaultProps 属性
* 这个属性中存储的是 props 对象中属性的默认值
* 遍历 defaultProps 对象 查看对应的 props 属性的值是否为 undefined
* 如果为 undefined 就将默认值赋值给对应的 props 属性值
*/
// 将 type 属性值视为函数 查看其中是否具备 defaultProps 属性
if (type && type.defaultProps) {
// 将 type 函数下的 defaultProps 属性赋值给 defaultProps 变量
const defaultProps = type.defaultProps;
// 遍历 defaultProps 对象中的属性 将属性名称赋值给 propName 变量
for (propName in defaultProps) {
// 如果 props 对象中的该属性的值为 undefined
if (props[propName] === undefined) {
// 将 defaultProps 对象中的对应属性的值赋值给 props 对象中的对应属性的值
props[propName] = defaultProps[propName];
}
}
}
/**
* 在开发环境中 如果元素的 key 属性 或者 ref 属性存在
* 监测开发者是否在组件外部通过 props 对象获取了 key 属性或者 ref 属性
* 如果获取了 就报错
*/
// 如果处于开发环境
if (__DEV__) {
// 元素具备 key 属性或者 ref 属性
if (key || ref) {
// 看一下 type 属性中存储的是否是函数 如果是函数就示意以后元素是组件
// 如果元素不是组件 就间接返回元素类型字符串
// displayName 用于在报错过程中显示是哪一个组件报错了
// 如果开发者显式定义了 displayName 属性 就显示开发者定义的
// 否者就显示组件名称 如果组件也没有名称 就显示 'Unknown'
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
// 如果 key 属性存在
if (key) {
// 为 props 对象增加 key 属性
// 并指定当通过 props 对象获取 key 属性时报错
defineKeyPropWarningGetter(props, displayName);
}
// 如果 ref 属性存在
if (ref) {
// 为 props 对象增加 ref 属性
// 并指定当通过 props 对象获取 ref 属性时报错
defineRefPropWarningGetter(props, displayName);
}
}
}
// 返回 ReactElement
return ReactElement(
type,
key,
ref,
self,
source,
// 在 Virtual DOM 中用于辨认自定义组件
ReactCurrentOwner.current,
props,
);
}
ReactElement
/**
* 接管参数 返回 ReactElement
*/
const ReactElement = function (type, key, ref, self, source, owner, props) {
const element = {
/**
* 组件的类型, 十六进制数值或者 Symbol 值
* React 在最终在渲染 DOM 的时候, 须要确保元素的类型是 REACT_ELEMENT_TYPE
* 须要此属性作为判断的根据
*/
$$typeof: REACT_ELEMENT_TYPE,
/**
* 元素具体的类型值 如果是元素节点 type 属性中存储的就是 div span 等等
* 如果元素是组件 type 属性中存储的就是组件的构造函数
*/
type: type,
/**
* 元素的惟一标识
* 用作外部 vdom 比对 晋升 DOM 操作性能
*/
key: key,
/**
* 存储元素 DOM 对象或者组件 实例对象
*/
ref: ref,
/**
* 存储向组件外部传递的数据
*/
props: props,
/**
* 记录以后元素所属组件 (记录以后元素是哪个组件创立的)
*/
_owner: owner,
};
// 返回 ReactElement
return element;
};
该办法波及到的 一些辅助函数 也都在这个文件中 大家能够下载下来源代码 而后看一下
虚构 dom 解析进去大略是这样
能够参考我之前写的解析进去的构造 是相似的
而后就是咱们转换进去的 fiber 节点的构造
type Fiber = {
/************************ DOM 实例相干 *****************************/
// 标记不同的组件类型, 值详见 WorkTag
tag: WorkTag,
// 组件类型 div、span、组件构造函数
type: any,
// 实例对象, 如类组件的实例、原生 dom 实例, 而 function 组件没有实例, 因而该属性是空
stateNode: any,
/************************ 构建 Fiber 树相干 ***************************/
// 指向本人的父级 Fiber 对象
return: Fiber | null,
// 指向本人的第一个子级 Fiber 对象
child: Fiber | null,
// 指向本人的下一个兄弟 iber 对象
sibling: Fiber | null,
// 在 Fiber 树更新的过程中,每个 Fiber 都会有一个跟其对应的 Fiber
// 咱们称他为 current <==> workInProgress
// 在渲染实现之后他们会替换地位
// alternate 指向以后 Fiber 在 workInProgress 树中的对应 Fiber
alternate: Fiber | null,
/************************ 状态数据相干 ********************************/
// 行将更新的 props
pendingProps: any,
// 旧的 props
memoizedProps: any,
// 旧的 state
memoizedState: any,
/************************ 副作用相干 ******************************/
// 该 Fiber 对应的组件产生的状态更新会寄存在这个队列外面 组件的状态
updateQueue: UpdateQueue<any> | null,
// 用来记录以后 Fiber 要执行的 DOM 操作
effectTag: SideEffectTag,
// 存储要执行的 DOM 操作
firstEffect: Fiber | null,
// 单链表用来疾速查找下一个 side effect
nextEffect: Fiber | null,
// 存储 DOM 操作完后的副租用 比方调用生命周期函数或者钩子函数的调用
lastEffect: Fiber | null,
// 工作的过期工夫 用于优先级 计算
expirationTime: ExpirationTime,
// 以后组件及子组件处于何种渲染模式 详见 TypeOfMode
mode: TypeOfMode,
};
接来下 就该正式进入了,首先咱们 react 的 app 个别都是以 ReactDOM.render 挂载办法作为入口
咱们先来讲下首次渲染的状况
接下来咱们找到 render 函数
render
文件地位:packages/react-dom/src/client/ReactDOMLegacy.js
/**
* 渲染入口
* element 要进行渲染的 ReactElement, createElement 办法的返回值
* container 渲染容器 <div id="root"></div>
* callback 渲染实现后执行的回调函数
*/
export function render(
element: React$Element<any>,
container: Container,
callback: ?Function,
) {
// 检测 container 是否是符合要求的渲染容器
// 即检测 container 是否是实在的 DOM 对象
// 如果不符合要求就报错
invariant(isValidContainer(container),
'Target container is not a DOM element.',
);
return legacyRenderSubtreeIntoContainer(
// 父组件 初始渲染没有父组件 传递 null 占位
null,
element,
container,
// 是否为服务器端渲染 false 不是服务器端渲染 true 是服务器端渲染
false,
callback,
);
}
因为咱们是串通主流程 所以间接看 legacyRenderSubtreeIntoContainer 办法
legacyRenderSubtreeIntoContainer
这个办法用来生成 FiberRoot,这个外面会创立 Root 而后它上面_internalRoot 会贮存 fiberRoot。
文件地位: packages/react-dom/src/client/ReactDOMLegacy.js
/**
* 将子树渲染到容器中 (初始化 Fiber 数据结构: 创立 fiberRoot 及 rootFiber)
* parentComponent: 父组件, 初始渲染传入了 null
* children: render 办法中的第一个参数, 要渲染的 ReactElement
* container: 渲染容器
* forceHydrate: true 为服务端渲染, false 为客户端渲染
* callback: 组件渲染实现后须要执行的回调函数
**/
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: Container,
forceHydrate: boolean,
callback: ?Function,
) {
/**
* 检测 container 是否曾经是初始化过的渲染容器
* react 在初始渲染时会为最外层容器增加 _reactRootContainer 属性
* react 会依据此属性进行不同的渲染形式
* root 不存在 示意初始渲染
* root 存在 示意更新
*/
// 获取 container 容器对象下是否有 _reactRootContainer 属性
let root: RootType = (container._reactRootContainer: any);
// 行将存储根 Fiber 对象
let fiberRoot;
if (!root) {
// 初始渲染
// 初始化根 Fiber 数据结构
// 为 container 容器增加 _reactRootContainer 属性
// 在 _reactRootContainer 对象中有一个属性叫做 _internalRoot
// _internalRoot 属性值即为 FiberRoot 示意根节点 Fiber 数据结构
// legacyCreateRootFromDOMContainer
// createLegacyRoot
// new ReactDOMBlockingRoot -> this._internalRoot
// createRootImpl
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
// 获取 Fiber Root 对象
fiberRoot = root._internalRoot;
/**
* 扭转 callback 函数中的 this 指向
* 使其指向 render 办法第一个参数的实在 DOM 对象
*/
// 如果 callback 参数是函数类型
if (typeof callback === 'function') {
// 应用 originalCallback 存储 callback 函数
const originalCallback = callback;
// 为 callback 参数从新赋值
callback = function () {
// 获取 render 办法第一个参数的实在 DOM 对象
// 实际上就是 id="root" 的 div 的子元素
// rootFiber.child.stateNode
// rootFiber 就是 id="root" 的 div
const instance = getPublicRootInstance(fiberRoot);
// 调用 callback 函数并扭转函数外部 this 指向
originalCallback.call(instance);
};
}
// 初始化渲染不执行批量更新
// 因为批量更新是异步的是能够被打断的, 然而初始化渲染应该尽快实现不能被打断
// 所以不执行批量更新
unbatchedUpdates(() => {updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
// 非初始化渲染 即更新
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
// Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
// 返回 render 办法第一个参数的实在 DOM 对象作为 render 办法的返回值
// 就是说渲染谁 返回谁的实在 DOM 对象
return getPublicRootInstance(fiberRoot);
}
legacyCreateRootFromDOMContainer 该会办法会清空 容器 的元素 (占位元素) 并创立 fiberRoot
看主流程 这个办法里最终是调用的 unbatchedUpdates 这个办法 初始执行的时候 会间接调用回调函数 也就是会间接调用 updateContainer 办法。
updateContainer
这个办法将工作 (Update) 寄存于工作队列 (updateQueue) 中 串并进行串联
并且 调度和更新 current 对象(render 阶段构建 workInProgress 树 比照生成 dom 构造或筛选有更新的 fiber 节点 commit 阶段开始更新)
文件地位: packages/react-reconciler/src/ReactFiberReconciler.js
/**
* 计算工作的过期工夫
* 再依据工作过期工夫创立 Update 工作
*/
export function updateContainer(
// element 要渲染的 ReactElement
element: ReactNodeList,
// container Fiber Root 对象
container: OpaqueRoot,
// parentComponent 父组件 初始渲染为 null
parentComponent: ?React$Component<any, any>,
// ReactElement 渲染实现执行的回调函数
callback: ?Function,
): ExpirationTime {
// container 获取 rootFiber
const current = container.current;
// 获取以后间隔 react 利用初始化的工夫 1073741805
const currentTime = requestCurrentTimeForUpdate();
// 异步加载设置
const suspenseConfig = requestCurrentSuspenseConfig();
// 计算过期工夫
// 为避免工作因为优先级的起因始终被打断而未能执行
// react 会设置一个过期工夫, 当工夫到了过期工夫的时候
// 如果工作还未执行的话, react 将会强制执行该工作
// 初始化渲染时, 工作同步执行不波及被打断的问题 1073741823
const expirationTime = computeExpirationForFiber(
currentTime,
current,
suspenseConfig,
);
// 设置 FiberRoot.context, 首次执行返回一个 emptyContext, 是一个 {}
const context = getContextForSubtree(parentComponent);
// 初始渲染时 Fiber Root 对象中的 context 属性值为 null
// 所以会进入到 if 中
if (container.context === null) {// 初始渲染时将 context 属性值设置为 {}
container.context = context;
} else {container.pendingContext = context;}
// 创立一个待执行工作
const update = createUpdate(expirationTime, suspenseConfig);
// 将要更新的内容挂载到更新对象中的 payload 中
// 将要更新的组件存储在 payload 对象中, 不便前期获取
update.payload = {element};
// 判断 callback 是否存在
callback = callback === undefined ? null : callback;
// 如果 callback 存在
if (callback !== null) {
// 将 callback 挂载到 update 对象中
// 其实就是一层层传递 不便 ReactElement 元素渲染实现调用
// 回调函数执行实现后会被革除 能够在代码的前面加上 return 进行验证
update.callback = callback;
}
// 将 update 对象退出到以后 Fiber 的更新队列当中 (updateQueue)
enqueueUpdate(current, update);
// 调度和更新 current 对象
scheduleWork(current, expirationTime);
// 返回过期工夫
return expirationTime;
}
enqueueUpdate
文件地位: packages/react-reconciler/src/ReactUpdateQueue.js
// 将工作 (Update) 寄存于工作队列 (updateQueue) 中
// 创立单向链表构造寄存 update, next 用来串联 update
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
// 获取以后 Fiber 的 更新队列
const updateQueue = fiber.updateQueue;
// 如果更新队列不存在 就返回 null
if (updateQueue === null) {
// 仅产生在 fiber 曾经被卸载
return;
}
// 获取待执行的 Update 工作
// 初始渲染时没有待执行的工作
const sharedQueue = updateQueue.shared;
const pending = sharedQueue.pending;
// 如果没有待执行的 Update 工作
if (pending === null) {
// 这是第一次更新, 创立一个循环列表.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
// 将 Update 工作存储在 pending 属性中
sharedQueue.pending = update;
}
scheduleWork as scheduleUpdateOnFiber
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
/**
* 判断工作是否为同步 调用同步工作入口
*/
export function scheduleUpdateOnFiber(
// rootFiber
fiber: Fiber,
expirationTime: ExpirationTime,
) {
/**
* fiber: 初始化渲染时为 rootFiber, 即 <div id="root"></div> 对应的 Fiber 对象
* expirationTime: 工作过期工夫 =>1073741823
*/
/**
* 判断是否是有限循环的 update 如果是就报错
* 在 componentWillUpdate 或者 componentDidUpdate 生命周期函数中反复调用
* setState 办法时, 可能会产生这种状况, React 限度了嵌套更新的数量以避免有限循环
* 限度的嵌套更新数量为 50, 可通过 NESTED_UPDATE_LIMIT 全局变量获取
*/
checkForNestedUpdates();
// 判断工作是否是同步工作 Sync 的值为: 1073741823
if (expirationTime === Sync) {
if (
// 查看是否处于非批量更新模式
(executionContext & LegacyUnbatchedContext) !== NoContext &&
// 查看是否没有处于正在进行渲染的工作
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
// 同步工作入口点
performSyncWorkOnRoot(root);
}
// 疏忽了一些初始化渲染不会失去执行的代码
}
首次加载因为要让用户尽快看到界面 所以是一个同步工作
会走 performSyncWorkOnRoot 这个同步工作入口
performSyncWorkOnRoot
正式进入 render 阶段 开始构建 workInProgress 树
// 进入 render 阶段, 构建 workInProgress Fiber 树
function performSyncWorkOnRoot(root) {
// 参数 root 为 fiberRoot 对象
// 查看是否有过期的工作
// 如果没有过期的工作 值为 0
// 初始化渲染没有过期的工作待执行
const lastExpiredTime = root.lastExpiredTime;
// NoWork 值为 0
// 如果有过期的工作 将过期工夫设置为 lastExpiredTime 否则将过期工夫设置为 Sync
// 初始渲染过期工夫被设置成了 Sync
const expirationTime = lastExpiredTime !== NoWork ? lastExpiredTime : Sync;
// 如果 root 和 workInProgressRoot 不相等
// 阐明 workInProgressRoot 不存在, 阐明还没有构建 workInProgress Fiber 树
// workInProgressRoot 为全局变量 默认值为 null, 初始渲染时值为 null
// expirationTime => 1073741823
// renderExpirationTime => 0
// true
if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {
// 构建 workInProgressFiber 树及 rootFiber
prepareFreshStack(root, expirationTime);
}
// workInProgress 如果不为 null
if (workInProgress !== null) {
do {
try {
// 以同步的形式开始构建 Fiber 对象
workLoopSync();
// 跳出 while 循环
break;
} catch (thrownValue) {handleError(root, thrownValue);
}
} while (true);
if (workInProgress !== null) {
// 这是一个同步渲染, 所以咱们应该实现整棵树.
// 无奈提交不残缺的 root, 此谬误可能是因为 React 中的谬误所致. 请提出问题.
invariant(
false,
'Cannot commit an incomplete root. This error is likely caused by a' +
'bug in React. Please file an issue.',
);
} else {
// 将构建好的新 Fiber 对象存储在 finishedWork 属性中
// 提交阶段应用
root.finishedWork = (root.current.alternate: any);
root.finishedExpirationTime = expirationTime;
// 完结 render 阶段
// 进入 commit 阶段
finishSyncRender(root);
}
}
}
prepareFreshStack
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
构建 workInProgress Fiber 树中的 rootFiber
/**
* 依据 currentFiber 树中的 rootFiber
* 构建 workInProgressFiber 树中的 rootFiber
*/
function prepareFreshStack(root, expirationTime) {
// 为 FiberRoot 对象增加 finishedWork 属性
// finishedWork 示意 render 阶段执行实现后构建的待提交的 Fiber 对象
root.finishedWork = null;
// 初始化 finishedExpirationTime 值为 0
root.finishedExpirationTime = NoWork;
// 建构 workInProgress Fiber 树的 Fiber 对象
workInProgressRoot = root;
// 构建 workInProgress Fiber 树中的 rootFiber
workInProgress = createWorkInProgress(root.current, null);
renderExpirationTime = expirationTime;
workInProgressRootExitStatus = RootIncomplete;
}
createWorkInProgress
文件地位: packages/react-reconciler/src/ReactFiber.js
// 构建 workInProgress Fiber 树中的 rootFiber
// 构建实现后会替换 current fiber
// 初始渲染 pendingProps 为 null
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
// current: current Fiber 中的 rootFiber
// 获取 current Fiber 中对应的 workInProgress Fiber
let workInProgress = current.alternate;
// 如果 workInProgress 不存在
if (workInProgress === null) {
// 创立 fiber 对象
workInProgress = createFiber(
current.tag,
pendingProps,
current.key,
current.mode,
);
// 属性复用
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
workInProgress.stateNode = current.stateNode;
// 应用 alternate 存储 current
workInProgress.alternate = current;
// 应用 alternate 存储 workInProgress
current.alternate = workInProgress;
}
workInProgress.childExpirationTime = current.childExpirationTime;
workInProgress.expirationTime = current.expirationTime;
workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
// 返回创立好的 workInProgress Fiber 对象
return workInProgress;
}
workLoopSync
依据 rootFiber 开始构建 逐级构建 整颗 fiber 树 循环构建
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
// 它的值不为 null 意味着该 fiber 对象上依然有更新要执行
while (workInProgress !== null) {workInProgress = performUnitOfWork(workInProgress);
}
}
performUnitOfWork
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
这里是 承接 递的过程从父到子 构建(child 和兄弟) 以及 归的过程 从子到父
function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
// unitOfWork => workInProgress Fiber 树中的 rootFiber
// current => currentFiber 树中的 rootFiber
const current = unitOfWork.alternate;
// 存储下一个要构建的子级 Fiber 对象
let next;
// false
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {// 初始渲染 不执行} else {
// beginWork: 从父到子, 构建 Fiber 节点对象
// 返回值 next 为以后节点的子节点
next = beginWork(current, unitOfWork, renderExpirationTime);
}
// 为旧的 props 属性赋值
// 此次更新后 pendingProps 变为 memoizedProps
unitOfWork.memoizedProps = unitOfWork.pendingProps;
// 如果子节点不存在阐明以后节点向下遍历子节点曾经到底了
// 持续向上返回 遇到兄弟节点 构建兄弟节点的子 Fiber 对象 直到返回到根 Fiber 对象
if (next === null) {
// 从子到父, 构建其余节点 Fiber 对象
next = completeUnitOfWork(unitOfWork);
}
return next;
}
beginWork
递 从父到子
文件地位: packages/react-reconciler/src/ReactFiberBeginWork.js
// 从父到子, 构建 Fiber 节点对象
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
): Fiber | null {
// NoWork 常量 值为 0 清空过期工夫
workInProgress.expirationTime = NoWork;
// 依据以后 Fiber 的类型决定如何构建起子级 Fiber 对象
// 文件地位: shared/ReactWorkTags.js
switch (workInProgress.tag) {
// 2
// 函数型组件在第一次渲染组件时应用
case IndeterminateComponent: {
return mountIndeterminateComponent(
// 旧 Fiber
current,
// 新 Fiber
workInProgress,
// 新 Fiber 的 type 值 初始渲染时是 App 组件函数
workInProgress.type,
// 同步 整数最大值 1073741823
renderExpirationTime,
);
}
// 0
case FunctionComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
}
// 1
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
}
// 3
case HostRoot:
return updateHostRoot(current, workInProgress, renderExpirationTime);
// 5
case HostComponent:
return updateHostComponent(current, workInProgress, renderExpirationTime);
// 6
case HostText:
return updateHostText(current, workInProgress);
// 组件类型未知 报错
invariant(
false,
'Unknown unit of work tag (%s). This error is likely caused by a bug in' +
'React. Please file an issue.',
workInProgress.tag,
);
}
updateHostRoot
首次加载会匹配 HostRoot 而后会执行 updateHostRoot 函数
文件地位: packages/react-reconciler/src/ReactFiberBeginWork.js
// HostRoot => <div id="root"></div> 对应的 Fiber 对象
// 找出 HostRoot 的子 ReactElement 并为其构建 Fiber 对象
function updateHostRoot(current, workInProgress, renderExpirationTime) {
// 获取更新队列
const updateQueue = workInProgress.updateQueue;
// 获取新的 props 对象 null
const nextProps = workInProgress.pendingProps;
// 获取上一次渲染应用的 state null
const prevState = workInProgress.memoizedState;
// 获取上一次渲染应用的 children null
const prevChildren = prevState !== null ? prevState.element : null;
// 浅复制更新队列, 避免援用属性相互影响
// workInProgress.updateQueue 浅拷贝 current.updateQueue
cloneUpdateQueue(current, workInProgress);
// 获取 updateQueue.payload 并赋值到 workInProgress.memoizedState
// 要更新的内容就是 element 就是 rootFiber 的子元素
processUpdateQueue(workInProgress, nextProps, null, renderExpirationTime);
// 获取 element 所在对象
const nextState = workInProgress.memoizedState;
// 从对象中获取 element
const nextChildren = nextState.element;
// 获取 fiberRoot 对象
const root: FiberRoot = workInProgress.stateNode;
// 服务器端渲染走 if
if (root.hydrate && enterHydrationState(workInProgress)) {// 疏忽} else {
// 客户端渲染走 else
// 构建子节点 fiber 对象
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
}
// 返回子节点 fiber 对象
return workInProgress.child;
}
能够看到该办法会调用 reconcileChildren 构建子级和兄弟节点 (和咱们 mini-fiber 相似)
最初返回子级
这个实现逻辑 https://segmentfault.com/a/11…
就是我这篇 mini-fiber 的的构建过程
找到没有子级之后,返回到父级 而后找父级的兄弟节点 再持续反复这个构建过程
reconcileChildren
文件地位: packages/react-reconciler/src/ReactFiberBeginWork.js
export function reconcileChildren(
// 旧 Fiber
current: Fiber | null,
// 父级 Fiber
workInProgress: Fiber,
// 子级 vdom 对象
nextChildren: any,
// 初始渲染 整型最大值 代表同步工作
renderExpirationTime: ExpirationTime,
) {
/**
* 为什么要传递 current ?
* 如果不是初始渲染的状况, 要进行新旧 Fiber 比照
* 初始渲染时则用不到 current
*/
// 如果就 Fiber 为 null 示意初始渲染
if (current === null) {
// 为以后构建的 Fiber 对象增加子级 Fiber 对象
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderExpirationTime,
);
}
// 疏忽了 else 的状况
}
mountChildFibers
文件地位: packages/react-reconciler/src/ReactChildFiber.js
/**
* shouldTrackSideEffects 标识, 是否为 Fiber 对象增加 effectTag
* true 增加 false 不增加
* 对于初始渲染来说, 只有根组件须要增加, 其余元素不须要增加, 避免过多的 DOM 操作
*/
// 用于初始渲染
export const mountChildFibers = ChildReconciler(false);
function ChildReconciler(shouldTrackSideEffects) {
function placeChild(
newFiber: Fiber,
lastPlacedIndex: number,
newIndex: number,
): number {
newFiber.index = newIndex;
if (!shouldTrackSideEffects) {return lastPlacedIndex;}
// 疏忽了一部分初始化渲染不执行的代码
}
function placeSingleChild(newFiber: Fiber): Fiber {// 如果是初始渲染 会在根组件 (App) 上设置 effectTag 属性为 Placement 值为 1
// 其余子级节点具备默认值为 0 避免在 commit 阶段重复操作实在 DOM
// 初始渲染时如果以后解决的是根组件 true 其余组件 false
if (shouldTrackSideEffects && newFiber.alternate === null) {
// Placement 示意新创建的节点
newFiber.effectTag = Placement;
}
return newFiber;
}
// 解决子元素是数组的状况
function reconcileChildrenArray(
// 父级 Fiber
returnFiber: Fiber,
currentFirstChild: Fiber | null,
// 子级 vdom 数组
newChildren: Array<*>,
expirationTime: ExpirationTime,
): Fiber | null {
/**
* 存储第一个子节点 Fiber 对象
* 办法返回的也是第一个子节点 Fiber 对象
* 因为其余子节点 Fiber 对象都存储在上一个子 Fiber 节点对象的 sibling 属性中
*/
let resultingFirstChild: Fiber | null = null;
// 上一次创立的 Fiber 对象
let previousNewFiber: Fiber | null = null;
// 初始渲染没有旧的子级 所以为 null
let oldFiber = currentFirstChild;
let lastPlacedIndex = 0;
let newIdx = 0;
let nextOldFiber = null;
// oldFiber 为空 阐明是初始渲染
if (oldFiber === null) {
// 遍历子 vdom 对象
for (; newIdx < newChildren.length; newIdx++) {
// 创立子 vdom 对应的 fiber 对象
const newFiber = createChild(
returnFiber,
newChildren[newIdx],
expirationTime,
);
// 如果 newFiber 为 null
if (newFiber === null) {
// 进入下次循环
continue;
}
// 初始渲染时只为 newFiber 增加了 index 属性,
// 其余事没干. lastPlacedIndex 被一成不变的返回了
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
// 为以后节点设置下一个兄弟节点
if (previousNewFiber === null) {
// 存储第一个子 Fiber 产生在第一次循环时
resultingFirstChild = newFiber;
} else {
// 为节点设置下一个兄弟 Fiber
previousNewFiber.sibling = newFiber;
}
// 在循环的过程中更新上一个创立的 Fiber 对象
previousNewFiber = newFiber;
}
// 返回创立好的子 Fiber
// 其余 Fiber 都作为 sibling 存在
return resultingFirstChild;
}
// 返回第一个子元素 Fiber 对象
return resultingFirstChild;
}
// 解决子元素是文本或者数值的状况
function reconcileSingleTextNode(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
textContent: string,
expirationTime: ExpirationTime,
): Fiber {
// 初始渲染不执行
if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
// We already have an existing node so let's just update it and delete
// the rest.
deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
const existing = useFiber(currentFirstChild, textContent);
existing.return = returnFiber;
return existing;
}
// 现有的第一个子节点不是文本节点,因而咱们须要创立一个并删除现有的.
// 初始渲染不执行
deleteRemainingChildren(returnFiber, currentFirstChild);
// 依据文本创立 Fiber 对象
const created = createFiberFromText(
textContent,
returnFiber.mode,
expirationTime,
);
// 设置父 Fiber 对象
created.return = returnFiber;
// 返回创立好的 Fiber 对象
return created;
}
// 解决子元素是单个对象的状况
function reconcileSingleElement(
// 父 Fiber 对象
returnFiber: Fiber,
// 备份子 fiber
currentFirstChild: Fiber | null,
// 子 vdom 对象
element: ReactElement,
expirationTime: ExpirationTime,
): Fiber {
// 查看子 vdom 对象是否示意 fragment
if (element.type === REACT_FRAGMENT_TYPE) {// false} else {
// 依据 React Element 创立 Fiber 对象
// 返回创立好的 Fiber 对象
const created = createFiberFromElement(
element,
// 用来示意以后组件下的所有子组件要用处于何种渲染模式
// 文件地位: ./ReactTypeOfMode.js
// 0 同步渲染模式
// 100 异步渲染模式
returnFiber.mode,
expirationTime,
);
// 增加 ref 属性 {current: DOM}
created.ref = coerceRef(returnFiber, currentFirstChild, element);
// 增加父级 Fiber 对象
created.return = returnFiber;
// 返回创立好的子 Fiber
return created;
}
}
function reconcileChildFibers(
// 父 Fiber 对象
returnFiber: Fiber,
// 旧的第一个子 Fiber 初始渲染 null
currentFirstChild: Fiber | null,
// 新的子 vdom 对象
newChild: any,
// 初始渲染 整型最大值 代表同步工作
expirationTime: ExpirationTime,
): Fiber | null {
// 这是入口办法, 依据 newChild 类型进行对应解决
// 判断新的子 vdom 是否为占位组件 比方 <></>
// false
const isUnkeyedTopLevelFragment =
typeof newChild === 'object' &&
newChild !== null &&
newChild.type === REACT_FRAGMENT_TYPE &&
newChild.key === null;
// 如果 newChild 为占位符, 应用 占位符组件的子元素作为 newChild
if (isUnkeyedTopLevelFragment) {newChild = newChild.props.children;}
// 检测 newChild 是否为对象类型
const isObject = typeof newChild === 'object' && newChild !== null;
// newChild 是单个对象的状况
if (isObject) {
// 匹配子元素的类型
switch (newChild.$$typeof) {
// 子元素为 ReactElement
case REACT_ELEMENT_TYPE:
// 为 Fiber 对象设置 effectTag 属性
// 返回创立好的子 Fiber
return placeSingleChild(
// 解决单个 React Element 的状况
// 外部会调用其余办法创立对应的 Fiber 对象
reconcileSingleElement(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
),
);
}
}
// 解决 children 为文本和数值的状况 return "App works"
if (typeof newChild === 'string' || typeof newChild === 'number') {
return placeSingleChild(
reconcileSingleTextNode(
returnFiber,
currentFirstChild,
// 如果 newChild 是数值, 转换为字符串
'' + newChild,
expirationTime,
),
);
}
// children 是数组的状况
if (isArray(newChild)) {
// 返回创立好的子 Fiber
return reconcileChildrenArray(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
);
}
}
}
completeUnitOfWork
下一步是从子到父
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
/**
*
* 从下至上挪动到该节点的兄弟节点, 如果始终往上没有兄弟节点就返回父节点, 最终会达到 root 节点
* 1. 创立其余节点的 Fiber 对象
* 2. 创立每一个节点的实在 DOM 对象并将其增加到 stateNode 属性中
* 3. 构建 effect 链表构造
*/
function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
// 为 workInProgress 全局变量从新赋值
workInProgress = unitOfWork;
do {
// 获取备份节点
// 初始化渲染 非根 Fiber 对象没有备份节点 所以 current 为 null
const current = workInProgress.alternate;
// 父级 Fiber 对象, 非根 Fiber 对象都有父级
const returnFiber = workInProgress.return;
// 判断传入的 Fiber 对象是否构建实现, 任务调度相干
// & 是示意位的与运算, 把左右两边的数字转化为二进制
// 而后每一位别离进行比拟, 如果相等就为 1, 不相等即为 0
// 此处利用 "位与" 运算符的目标是 "清零"
// true
if ((workInProgress.effectTag & Incomplete) === NoEffect) {
let next;
// 如果不能应用分析器的 timer, 间接执行 completeWork
// enableProfilerTimer => true
// 但此处无论条件是否成立都会执行 completeWork
if (
!enableProfilerTimer ||
(workInProgress.mode & ProfileMode) === NoMode
) {// 重点代码(二)
// 创立节点实在 DOM 对象并将其增加到 stateNode 属性中
next = completeWork(current, workInProgress, renderExpirationTime);
} else {
// 创立节点实在 DOM 对象并将其增加到 stateNode 属性中
next = completeWork(current, workInProgress, renderExpirationTime);
}
// 重点代码(一)
// 如果子级存在
if (next !== null) {
// 返回子级 始终返回到 workLoopSync
// 再从新执行 performUnitOfWork 构建子级 Fiber 节点对象
return next;
}
// 构建 effect 链表构造
// 如果不是根 Fiber 就是 true 否则就是 false
// 将子树和此 Fiber 的所有 effect 附加到父级的 effect 列表中
if (
// 如果父 Fiber 存在 并且
returnFiber !== null &&
// 父 Fiber 对象中的 effectTag 为 0
(returnFiber.effectTag & Incomplete) === NoEffect
) {
// 将子树和此 Fiber 的所有副作用附加到父级的 effect 列表上
// 以下两个判断的作用是收集子 Fiber 的 effect 到父 Fiber
if (returnFiber.firstEffect === null) {
// first
returnFiber.firstEffect = workInProgress.firstEffect;
}
if (workInProgress.lastEffect !== null) {if (returnFiber.lastEffect !== null) {
// next
returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
}
// last
returnFiber.lastEffect = workInProgress.lastEffect;
}
// 获取副作用标记
// 初始渲染时除 [根组件] 以外的 Fiber, effectTag 值都为 0, 即不须要执行任何实在 DOM 操作
// 根组件的 effectTag 值为 3, 即须要将此节点对应的实在 DOM 对象增加到页面中
const effectTag = workInProgress.effectTag;
// 创立 effect 列表时跳过 NoWork(0) 和 PerformedWork(1) 标记
// PerformedWork 由 React DevTools 读取, 不提交
// 初始渲染时 只有遍历到了根组件 判断条件能力成立, 将 effect 链表增加到 rootFiber
// 初始渲染 FiberRoot 对象中的 firstEffect 和 lastEffect 都是 App 组件
// 因为当所有节点在内存中构建实现后, 只须要一次将所有 DOM 增加到页面中
if (effectTag > PerformedWork) {
// false
if (returnFiber.lastEffect !== null) {returnFiber.lastEffect.nextEffect = workInProgress;} else {
// 为 fiberRoot 增加 firstEffect
returnFiber.firstEffect = workInProgress;
}
// 为 fiberRoot 增加 lastEffect
returnFiber.lastEffect = workInProgress;
}
}
} else {// 疏忽了初始渲染不执行的代码}
// 获取下一个同级 Fiber 对象
const siblingFiber = workInProgress.sibling;
// 如果下一个同级 Fiber 对象存在
if (siblingFiber !== null) {
// 返回下一个同级 Fiber 对象
return siblingFiber;
}
// 否则退回父级
workInProgress = returnFiber;
} while (workInProgress !== null);
// 当执行到这里的时候, 阐明遍历到了 root 节点, 已实现遍历
// 更新 workInProgressRootExitStatus 的状态为 已实现
if (workInProgressRootExitStatus === RootIncomplete) {workInProgressRootExitStatus = RootCompleted;}
return null;
}
completeWork
文件地位: packages/react-reconciler/src/ReactFiberCompleteWork.js
首次加载 归的时候 会把子级 dom 构造追加到父级中
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
): Fiber | null {
// 获取待更新 props
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
// 0
case FunctionComponent:
return null;
// 3
case HostRoot: {updateHostContainer(workInProgress);
return null;
}
// 5
case HostComponent: {
// 获取 rootDOM 节点 <div id="root"></div>
const rootContainerInstance = getRootHostContainer();
// 节点的具体的类型 div span ...
const type = workInProgress.type;
// false current = null
if (current !== null && workInProgress.stateNode != null) {// 初始渲染不执行} else {
// false
if (wasHydrated) {// 初始渲染不执行} else {
// 创立节点实例对象 <div></div> <span></span>
let instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
/**
* 将所有的子级追加到父级中
* instance 为父级
* workInProgress.child 为子级
*/
appendAllChildren(instance, workInProgress, false, false);
// 为 Fiber 对象增加 stateNode 属性
workInProgress.stateNode = instance;
}
// 解决 ref DOM 援用
if (workInProgress.ref !== null) {markRef(workInProgress);
}
}
return null;
}
}
}
appendAllChildren
文件地位: packages/react-reconciler/src/ReactFiberCompleteWork.js
appendAllChildren = function (
parent: Instance,
workInProgress: Fiber,
needsVisibilityToggle: boolean,
isHidden: boolean,
) {
// 获取子级
let node = workInProgress.child;
// 如果子级不为空 执行循环
while (node !== null) {
// 如果 node 是一般 ReactElement 或者为文本
if (node.tag === HostComponent || node.tag === HostText) {
// 将子级追加到父级中
appendInitialChild(parent, node.stateNode);
} else if (node.child !== null) {
// 如果 node 不是一般 ReactElement 又不是文本
// 将 node 视为组件, 组件自身不能转换为实在 DOM 元素
// 获取到组件的第一个子元素, 继续执行循环
node.child.return = node;
node = node.child;
continue;
}
// 如果 node 和 workInProgress 是同一个节点
// 阐明 node 曾经退回到父级 终止循环
// 阐明此时所有子级都曾经追加到父级中了
if (node === workInProgress) {return;}
// 解决子级节点的兄弟节点
while (node.sibling === null) {
// 如果节点没有父级或者节点的父级是本人, 退出循环
// 阐明此时所有子级都曾经追加到父级中了
if (node.return === null || node.return === workInProgress) {return;}
// 更新 node
node = node.return;
}
// 更新父级 不便回退
node.sibling.return = node.return;
// 将 node 更新为下一个兄弟节点
node = node.sibling;
}
};
归的过程完了之后 整体的 dom 构造 会被全副 收集全 到顶级的 fiber 中
当初到这里 workInProgress 树构建完了 也就是说 render 阶段 曾经完结了 接下来就是进入 commit 提交阶段了,上面
finishSyncRender
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
function finishSyncRender(root) {
// 销毁 workInProgress Fiber 树
// 因为待提交 Fiber 对象曾经被存储在了 root.finishedWork 中
workInProgressRoot = null;
// 进入 commit 阶段
commitRoot(root);
}
commitRoot
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
开始正式提交 commit
function commitRoot(root) {
// 获取工作优先级 97 => 一般优先级
const renderPriorityLevel = getCurrentPriorityLevel();
// 应用最高优先级执行当前任务, 因为 commit 阶段不能够被打断
// ImmediatePriority, 优先级为 99, 最高优先级
runWithPriority(
ImmediatePriority,
commitRootImpl.bind(null, root, renderPriorityLevel),
);
return null;
}
commitRootImpl
这里开始执行 commitRootImpl 办法
commit 阶段能够分为三个子阶段:
- before mutation 阶段(执行 DOM 操作前)
- mutation 阶段(执行 DOM 操作)
- layout 阶段(执行 DOM 操作后)
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
function commitRootImpl(root, renderPriorityLevel) {
// 获取待提交 Fiber 对象 rootFiber
const finishedWork = root.finishedWork;
// 如果没有工作要执行
if (finishedWork === null) {
// 阻止程序持续向下执行
return null;
}
// 重置为默认值
root.finishedWork = null;
root.callbackNode = null;
root.callbackExpirationTime = NoWork;
root.callbackPriority = NoPriority;
root.nextKnownPendingLevel = NoWork;
// 获取要执行 DOM 操作的副作用列表
let firstEffect = finishedWork.firstEffect;
// true
if (firstEffect !== null) {
// commit 第一个子阶段
nextEffect = firstEffect;
// 解决类组件的 getSnapShotBeforeUpdate 生命周期函数
do {invokeGuardedCallback(null, commitBeforeMutationEffects, null);
} while (nextEffect !== null);
// commit 第二个子阶段
nextEffect = firstEffect;
do {invokeGuardedCallback(null, commitMutationEffects, null, root, renderPriorityLevel);
} while (nextEffect !== null);
// 将 workInProgress Fiber 树变成 current Fiber 树
root.current = finishedWork;
// commit 第三个子阶段
nextEffect = firstEffect;
do {invokeGuardedCallback(null, commitLayoutEffects, null, root,expirationTime);
} while (nextEffect !== null);
// 重置 nextEffect
nextEffect = null;
}
}
第一子阶段
commitBeforeMutationEffects
件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
// commit 阶段的第一个子阶段
// 调用类组件的 getSnapshotBeforeUpdate 生命周期函数
function commitBeforeMutationEffects() {
// 循环 effect 链
while (nextEffect !== null) {
// nextEffect 是 effect 链上从 firstEffect 到 lastEffec 的每一个须要 commit 的 fiber 对象
// 初始化渲染第一个 nextEffect 为 App 组件
// effectTag => 3
const effectTag = nextEffect.effectTag;
// console.log(effectTag);
// nextEffect = null;
// return;
// 如果 fiber 对象中里有 Snapshot 这个 effectTag 的话
// Snapshot 和更新有关系 初始化渲染 不执行
// false
if ((effectTag & Snapshot) !== NoEffect) {
// 获取以后 fiber 节点
const current = nextEffect.alternate;
// 当 nextEffect 上有 Snapshot 这个 effectTag 时
// 执行以下办法, 次要是类组件调用 getSnapshotBeforeUpdate 生命周期函数
commitBeforeMutationEffectOnFiber(current, nextEffect);
}
// 更新循环条件
nextEffect = nextEffect.nextEffect;
}
}
commitBeforeMutationEffectOnFiber as commitBeforeMutationLifeCycles
文件地位: packages/react-reconciler/src/ReactFiberCommitWork.js
function commitBeforeMutationLifeCycles(
current: Fiber | null,
finishedWork: Fiber,
): void {switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block: {return;}
// 如果该 fiber 类型是 ClassComponent
case ClassComponent: {if (finishedWork.effectTag & Snapshot) {if (current !== null) {
// 旧的 props
const prevProps = current.memoizedProps;
// 旧的 state
const prevState = current.memoizedState;
// 获取 classComponent 组件的实例对象
const instance = finishedWork.stateNode;
// 执行 getSnapshotBeforeUpdate 生命周期函数
// 在组件更新前捕捉一些 DOM 信息
// 返回自定义的值或 null, 统称为 snapshot
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState,
);
}
}
return;
}
case HostRoot:
case HostComponent:
case HostText:
case HostPortal:
case IncompleteClassComponent:
// Nothing to do for these component types
return;
}
}
第二子阶段
commitMutationEffects
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
// commit 阶段的第二个子阶段
// 依据 effectTag 执行 DOM 操作
function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
// 循环 effect 链
while (nextEffect !== null) {
// 获取 effectTag
// 初始渲染第一次循环为 App 组件
// 行将根组件及外部所有内容一次性增加到页面中
const effectTag = nextEffect.effectTag;
// 依据 effectTag 别离解决
let primaryEffectTag =
effectTag & (Placement | Update | Deletion | Hydrating);
// 匹配 effectTag
// 初始渲染 primaryEffectTag 为 2 匹配到 Placement
switch (primaryEffectTag) {
// 针对该节点及子节点进行插入操作
case Placement: {commitPlacement(nextEffect);
// effectTag 从 3 变为 1
// 从 effect 标签中革除 "placement" 重置 effectTag 值
// 以便咱们晓得在调用诸如 componentDidMount 之类的任何生命周期之前已将其插入。nextEffect.effectTag &= ~Placement;
break;
}
// 插入并更新 DOM
case PlacementAndUpdate: {
// 插入
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement;
// 更新
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 服务器端渲染
case Hydrating: {
nextEffect.effectTag &= ~Hydrating;
break;
}
// 服务器端渲染
case HydratingAndUpdate: {
nextEffect.effectTag &= ~Hydrating;
// Update
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 更新 DOM
case Update: {
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 删除 DOM
case Deletion: {commitDeletion(root, nextEffect, renderPriorityLevel);
break;
}
}
nextEffect = nextEffect.nextEffect;
}
}