# react18.2
**Repository Path**: my_react_study/react18.2
## Basic Information
- **Project Name**: react18.2
- **Description**: react18.2源码学习
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 4
- **Created**: 2022-12-31
- **Last Updated**: 2025-02-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# react usage
>
## 源码调试
```js
// 1. 下载代码
git clone https://github.com/facebook/react.git
// https://github.com/facebook/react/tree/v18.2.0
// 2. 拷贝文件
- src\react\packages\react
- src\react\packages\react-dom
- src\react\packages\react-reconciler
- src\react\packages\scheduler
- src\react\packages\shared
// 3. 将flow文件改为js
npm install strip-flowtype -g
strip-flowtype src/react/packages/**/*.js
// 4. 删除 __text__测试目录
// 5. 修改配置
// src\react\packages\react-reconciler\src\ReactFiberHostConfig.js
export * from './forks/ReactFiberHostConfig.dom';
// src\react\packages\shared\ReactSharedInternals.js
import ReactSharedInternals from '../react/src/ReactSharedInternals';
export default ReactSharedInternals;
// src\react\packages\react\src\ReactServerContext.js
const ContextRegistry = ReactSharedInternals?.ContextRegistry;
```
## react 概念
```js
// react是什么
// react是一个用户构建用户界面的js库 组件化开发
// 组件化构建 快速响应的 大型web应用
// react和vue区别 优缺点
// react发展 react15 react16 react18
```
## jsx
```js
// 什么是jsx 和 template 区别
// jsx会经过babel的转义 https://www.babeljs.cn/repl
// 老版的React.createElement() 返回一个 React元素
// react17新版的会变为 jsx()的函数调用 返回的也是 ReactElement 元素
// 1. 新版不再需要 import React from 'react'
// 2. 两者对 children 的处理有差异 jsx_dev会将children放在props中
// 3. React.createElement() = jsx_dev()
// 1. jsx react元素 虚拟dom fiber 区别
// 1.1 编辑阶段: jsx通过babel编译得到 React.createElement()
// 1.2 在浏览器执行代码 React.createElement 函数会执行 返回 React 元素
// 1.3 React 元素就是一个普通的 js 对象 虚拟 dom
// 1.4 在react的workLoop的render阶段我们会根据 vdom 创建对应的fiber对象
// 2. babel如果实现的转义 ast语法树
```
## createElement
```js
// https://github.com/facebook/react/blob/main/packages/react/src/ReactElement.js
// 返回一个 ReactElement react元素
```
## jsx_dev
```js
// https://github.com/facebook/react/blob/main/packages/react/src/jsx/ReactJSXElement.js
// react/jsx-dev-runtime
// 和createElement是一样的 返回React元素就是我们说的虚拟dom
// babel的编译结果是不一样的 children不是一个个的在参数后面了
// children没单独处理 是在 props 中的
```
## render
```js
// react有两种渲染模式
// 在react18中默认是使用并发模式的
// 1. ReactDOM.render()
// 2. ReactDOM.createRoot().render()
```
## legacy
```js
// https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMLegacy.js
// render 函数
function render(element, container) {
return legacyRenderSubtreeIntoContainer(element, container);
}
// 区分初次挂载和更新
function legacyRenderSubtreeIntoContainer(children, container) {
const maybeRoot = container._reactRootContainer;
let root;
if (!maybeRoot) {
// 初始化挂载
root = legacyCreateRootFromDOMContainer(container, children);
} else {
root = updateContainer(children, root, parentComponent, callback);
}
// return getPublicRootInstance(root);
return root;
}
```
### Initial mount
```js
function legacyCreateRootFromDOMContainer(container, children) {
// 1. 创建 rootFiber和fiberRoot
const root = createContainer();
// 2. 事件系统
// listenToAllSupportedEvents();
// 3. 初始化挂载 同步
// ReactFiberWorkLoop flushSync设置优先级 执行fn
flushSync(updateContainer());
return root;
}
```
#### crateContainer
```js
// 创建rootFiber和fiberRoot 初始化更新队列 updateQueue(单向循环链表)
function createContainer(container) {
return createFiberRoot(containerInfo, tag, initialChildren);
}
// tag用来区分不同的模式
function createFiberRoot(containerInfo, tag) {
// 1. 创建 fiberRoot
const root = new FiberRootNode();
// 2. 创建 rootFiber 很多属性
const uninitializedFiber = createHostRootFiber();
// 3. 相互指向
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
uninitializedFiber.memoizedState = initialState;
// 4. 初始化更新队列
// fiber.updateQueue = queue = {shared: {pending: null}}
initializeUpdateQueue();
return root;
}
```
#### updateContainer
```js
// 创建更新 加入到更新队列 然后从根节点开始调度
function updateContainer(element, container) {
const current = element.current; // rootFiber
// 1. 计算lane
const eventTime = requestEventTime();
const lane = requestUpdateLane(current);
// 2. 创建一个update更新 {payload: null, next: null}
const update = createUpdate(eventTime, lane);
update.payload = { element };
// 3. 更新入对 按照批次加入到 concurrentQueues 数组中 return node.stateNode
// 两种不同的模式在updateQueue的处理上是有区别的
const root = enqueueUpdate(current, update, lane);
// 4. 从根节点开始调度
scheduleUpdateOnFiber(root, current, lane, eventTime);
return lane;
}
```
#### flushSync
```js
// 初次渲染在执行 flushSync(updateContainer())的时候
// 执行callback就是执行 updateContainer() 回调
function flushSync(fn) {
try {
if (fn) return fn();
} finally {
// 初次渲染会执行这个flush 直接清空
if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
flushSyncCallbacks();
}
}
}
// 清空 syncQueue 依次执行
function flushSyncCallbacks() {
const queue = syncQueue;
for (let i = 0; i < queue.length; i++) {
const callback = queue[i];
// 执行callback
do {
callback = callback(isSync);
} while (callback !== null);
}
}
```
### schedule
```js
// 在执行updateContainer的时候 将更新加入到updateQueue中然后从根节点开始调度
// ReactFiberWorkLoop
function scheduleUpdateOnFiber(root, fiber, lane, eventTime) {
// 1. 将root标记为更新 root.pendingLanes |= updateLane; 1
markRootUpdated(root, lane, eventTime);
// 2. 调度root
// 2.1 lane优先级 newCallbackPriority
// 2.2 将callback添加到 syncQueue 中 scheduleSyncCallback(performSyncWorkOnRoot添加到queue中)
// 2.3 在 microtask 中清空任务 queueMicrotask(flushSyncCallbacks)
// 2.3.1 设置优先级
// 2.3.2 依次执行 syncQueue中的回调 callback = callback(isSync);
// 2.3.3 执行 performSyncWorkOnRoot
ensureRootIsScheduled(root, eventTime);
}
// 要处理优先级问题 先跳过
function markRootUpdated() {}
function ensureRootIsScheduled(root, eventTime) {
// 1. 将cb加到如syncQueue中
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
// 2. 清空队列 flushSyncCallbacks就是取出 cb执行
// 初次渲染是在 flush的 finally 中直接就清空了
// 就是执行 performSyncWorkOnRoot
scheduleMicrotask(flushSyncCallbacks);
}
// 将cb加入到 syncQueue 中
let syncQueue = null;
function scheduleLegacySyncCallback(cb) {
if (syncQueue === null) syncQueue = [cb];
else syncQueue.push(cb);
}
```
### performSyncWorkOnRoot
```js
// workLoop 分为两个阶段
// 1. render阶段 异步可中断 计算副作用
// 2. commit提交阶段 同步的 提交副作用
function performSyncWorkOnRoot(root) {
// 1. render
const exitStatus = renderRootSync(root, lanes);
// 2. commit
commitRoot(root);
return null;
}
```
### renderRootSync
```js
// render阶段 开始 workLoop()
// 1. beginWork 根据虚拟dom节点判断tag递归的创建子fiber
// reconcileChildren dom-diff 创建fiber节点
// 2. completeWork 当child为null表示节点完成 根据不同的tag创建真实的dom节点 stateNode
// bubbleProperties 归并副作用 以前是收集effectList副作用链条
// 以fiber为工作单元的 异步可中断的操作
function renderRootSync() {
// 1. 创建 workProgress fiberRoot 双缓存
prepareFreshStack();
// 2. 开始工作循环
do {
workLoopSync();
} while (true);
return 0;
}
```
#### workLoopSync
```js
function performUnitOfWork() {
while (workInProgress !== null) {
// 开始工作单元
performUnitOfWork(workInProgress);
}
}
// 开始一个工作单元
function performUnitOfWork(unitOfWork) {
// fiber开始工作 返回第一个儿子
let next = beginWork(current, unitOfWork, renderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
// 当root节点 beginWork之后 返回第一个儿子 儿子开始beginWork(文本节点)
// 文本节点return null 就完成工作
if (next === null) {
// 如果没有了就表示自己完成了
// completeWork
completeUnitOfWork(unitOfWork);
} else {
// 执行下一个
workInProgress = next;
}
}
```
#### beginWork
```js
// ReactFiberBeginWork
function beginWork(current, workInProgress, renderLanes) {
// 根据不同的tag执行对应的方法
switch (
workInProgress.tag
case HostRoot: return updateHostRoot()
case HostText: return updateHostText()
) {
}
}
// 第一个根节点 beginWork的是 hostRoot
function updateHostRoot() {
// 1. 处理更新队列 将循环链表拆开 得到 newState
processUpdateQueue(workInProgress, nextProps, null, renderLanes);
// 2. 协调children 递归创建子fiber
reconcileChildren()
// 3. 返回大儿子
return workInProgress.child;
}
// 处理文本 直接返回 不创建fiber
function updateHostText() {
return null;
}
```
#### reconcileChildren
```js
// 这个方法很重要
// dom-diff
function reconcileChildren(current) {
if (current === null) {
// 初次挂载
workInProgress.child = mountChildFibers();
} else {
// 根节点是存在current的
workInProgress.child = reconcileChildFibers();
}
}
// ReactChildFiber
// 1. 文本的情况
function reconcileChildFibers() {
if (typeof newChild === "string" || typeof newChild === "number") {
// 1.创建fiber
// 2.增加flags
return placeSingleChild(
reconcileSingleTextNode(
returnFiber,
currentFirstChild,
"" + newChild,
lanes
)
);
}
}
// 创建 TextNode的fiber 维护fiber链条的关系
function reconcileSingleTextNode() {
const created = createFiberFromText();
created.return = returnFiber;
return created;
}
function placeSingleChild() {
newFiber.flags |= Placement;
}
```
#### completeUnitOfWork
```js
// 当 beginWork返回的是null表示这个节点工作完成了
function completeUnitOfWork(unitOfWork) {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
let next = completeWork(current, completedWork);
// 如果有next就继续工作
if (next !== null) {
workInProgress = next;
return;
}
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
workInProgress = siblingFiber;
return;
}
// 如果弟弟也完成了 就表示让parent完成 同时找叔叔开始工作
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
}
```
#### completeWork
```js
// ReactFiberCompleteWork
// beginWork是根据虚拟dom创建fiber链
// completeWork是根据fiber创建真实的dom节点 向上冒泡副作用(收集effectList副作用)
// 和beginWork一样需要判断tag做不同的处理 创建真实的dom节点
function completeWork() {
switch (workInProgress.tag) {
case HostText: {
// 先完成文本节点 document.createTextNode(text)
workInProgress.stateNode = createTextInstance(newText);
// 向上冒泡副作用 取代了之前的 effectList副作用链条
bubbleProperties(workInProgress);
return null;
}
case HostRoot: {
// 完成 root之后就全部完成了 执行 commitRoot的操作应用副作用
bubbleProperties(workInProgress);
}
}
}
```
#### bubbleProperties
```js
// subtreeFlags
function bubbleProperties(completedWork) {
let newChildLanes = NoLanes;
let subtreeFlags = NoFlags;
let child = completedWork.child;
while (child !== null) {
newChildLanes = mergeLanes(
newChildLanes,
mergeLanes(child.lanes, child.childLanes)
);
subtreeFlags |= child.subtreeFlags;
subtreeFlags |= child.flags;
child = child.sibling;
}
completedWork.subtreeFlags |= subtreeFlags;
completedWork.childLanes = newChildLanes;
// return didBailout;
}
```
### commitRoot
```js
// 提交阶段 ReactFiberWorkLoop
function commitRoot() {
if (subtreeHasEffects || rootHasEffect) {
// 1. before
// const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects()
// 2. 提交副作用
commitMutationEffects(root, finishedWork, lanes);
}
}
```
#### commitMutationEffects
```js
// 还是判断不同的tag做不同的处理 这里又需要大量的递归
function commitMutationEffects(finishedWork) {
switch (finishedWork.tag) {
case HostRoot:
// 递归提交
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
return;
case HostText: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
return;
}
default:
break;
}
}
// 递归
function recursivelyTraverseMutationEffects() {
let child = parentFiber.child;
while (child !== null) {
commitMutationEffectsOnFiber(child, root, lanes);
child = child.sibling;
}
}
// 提交 根据不同的flags执行不同的操作
function commitReconciliationEffects() {
if (flags & Placement) {
// 执行insert插入操作
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
}
}
```
## createRoot
```js
// https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOM.js
// react本身就是一个视图渲染器 将虚拟dom变成真实的dom
const root = createRoot(document.getElementById("root"));
root.render();
function createRoot(container) {
// 1. 创建root容器 createFiberRoot
// 模式是不一样的 fiber上的mode属性
// containerInfo:{div#root}
// rootFiber.stateNode = fiberRoot
// fiberRoot.current = rootFiber
const root = createContainer(container, ConcurrentMode);
// 2. node[internalContainerInstanceKey] = hostRoot;
// markContainerAsRoot(root.current, container);
// 3. 事件系统
// listenToAllSupportedEvents(rootContainerElement);
// 4. 返回react的根
// this._internalRoot = internalRoot;
// ReactDOMRoot.prototype.render = function(children) {}
return new ReactDOMRoot(root);
}
function ReactDOMRoot(internalRoot) {
this._internalRoot = internalRoot;
}
// render执行的也是 updateContainer
ReactDOMRoot.prototype.render = function (children) {
const root = this._internalRoot; // root
updateContainer(children, root, null, null);
};
```
### createContainer
[FiberRootNode](img/1.FiberRootNode.jpeg)
```js
// 创建fiberRoot
function createContainer() {
return createFiberRoot(containerInfo, tag;
}
function createFiberRoot() {
// fiberRoot div#root
const root = new FiberRootNode(containerInfo, tag);
// rootFiber 创建一个未初始化的 fiber
const uninitializedFiber = createHostRootFiber(tag);
// 相互指向
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
uninitializedFiber.memoizedState = initialState;
// 初始化更新队列 更新队列是一个循环链表
// fiber.updateQueue = queue = {shared: {pending: null}}
initializeUpdateQueue(uninitializedFiber);
return root;
}
```
### fiber
[renderFiber](img/2.renderFiber.jpeg)
```js
// 1. fiber是一个执行单元
// 作用单元是以fiber为基本的
// 2. fiber也是一种数据结构 链表 react中大量使用了链表 单向链表 双向链表 循环链表(更新队列)
// return child sibling
// react为什么要引入fiber
// 1. react15的架构是 Reconciler(协调器) + Renderer(渲染器)
// 1.1 reconciler 找出变化的组件
// 递归更新子组件 无法中断
// js执行过长会形成卡顿
// 1.2 renderer 将变化的组件渲染到页面上
// 2. react16
// 2.1 Scheduler(调度器)找到高优任务
// react和浏览器是合作调度方式;
// 优先级 expirationTime => lane模型
// 不使用 requestIdleCallback() react内部使用 MessageChannel 自己实现
// 1. 浏览器兼容性问题
// 2. 时间不可控 react每帧申请5ms
// 2.2 Reconciler(协调器) 找出变化的组件
// Reconciler与Renderer不再是交替工作 Reconciler将变化的节点加上flags标记
// 之前是递归处理 => 变成异步可中断的
// 引入fiber 浏览器及时响应用户的交互
// 2.3 Renderer(渲染器) 渲染页面
// fiber 时间切片
// js执行时间过长会操作卡顿的问题 出现性能问题
// 将一个大任务拆分成很多个小任务 分布在每一帧中
```
### root updateContainer
```js
// ReactDOM.render => flushSync(updateContainer());
// createContainer() => root.render() => updateContainer()
// => workLoop()
// => render阶段
// => beginWork根据虚拟dom构建fiber tree dom-diff
// => completer阶段 创建真实的dom节点 向上冒泡副作用
// => commit阶段 提交副作用 不可中断的
// https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberReconciler.js
function updateContainer(element, container) {
// 并发模式和传统的不一样
// 同步渲染返回的lane是 SyncLane
// 并发渲染 const updateLane = getCurrentEventPriority() 默认事件的优先级 16
const lane = requestUpdateLane(current);
// 1. 创建更新
const update = createUpdate(eventTime, lane);
update.payload = { element };
// 2. 将更新对象添加到current的更新队列上 updateQueue
// 构建循环链表 更新lanes 返回root根节点
// concurrent的模式是先添加到数组中
const root = enqueueUpdate(current, update, lane);
// 3. 开始调度
scheduleUpdateOnFiber(root, current, lane, eventTime);
return lane;
}
// ReactFiberClassUpdateQueue
function enqueueUpdate() {}
```
### scheduleUpdateOnFiber
```js
// 从根节点开始调度 并发模式和传统模式的区别 两者的lane不一样
// 同步的是Sync 并发的是 default 16 => 32 (加入了 SyncHydrationLane的lane)
function scheduleUpdateOnFiber() {
ensureRootIsScheduled(root, eventTime);
}
function ensureRootIsScheduled(root, currentTime) {
// const nextLanes = getNextLanes(); // root.pendingLanes
const newCallbackPriority = getHighestPriorityLane(nextLanes);
let newCallbackNode;
if (includesSyncLane(newCallbackPriority)) {
// 同步渲染的模式 执行的是 performSyncWorkOnRoot
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
scheduleMicrotask(flushSyncCallbacks());
newCallbackNode = null;
} else {
// 并发渲染 执行的是 performConcurrentWorkOnRoot
let schedulerPriorityLevel;
// lane对应调度优先级
switch (lanesToEventPriority(nextLanes)) {
}
// scheduleCallback 调度模块
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
}
}
// 调度模块 优先队列
function scheduleCallback(cb) {
// 暂时先使用 requestIdleCallback
requestIdleCallback(callback);
}
```
### performConcurrentWorkOnRoot
```js
// 开始工作循环
function performConcurrentWorkOnRoot(root) {
let lanes = getNextLanes();
// 初次渲染的时候肯定是同步的方式的
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
// 根据退出的状态进行判断 其实就是执行了 commitRoot()
finishConcurrentRender();
}
// 传统模式执行的也是 renderRootSync
function renderRootSync(root, lanes) {
// 1. 根据老fiber和属性创建新的fiber
// current和workInProgress
prepareFreshStack(root, lanes);
workLoopSync();
}
function finishConcurrentRender(root, exitStatus) {
switch (exitStatus) {
// 在 completeUnitOfWork 阶段完成之后会改为 RootCompleted
case RootCompleted: {
// 提交
commitRoot(root);
break;
}
default: {
throw new Error("Unknown root exit status.");
}
}
}
// 根据来的fiber和属性创建新的fiber
// current是老的 workInProgress是新的 正在工作的
function prepareFreshStack() {
const rootWorkInProgress = createWorkInProgress(root.current, null);
// 并发模式下的render阶段的退出状态
workInProgressRootExitStatus = RootInProgress;
// enqueueConcurrentClassUpdate 中的 enqueueUpdate将更新先一批批的添加到concurrentQueues数组中
// 在 prepareFreshStack 的时候在构建更新的循环链表
finishQueueingConcurrentUpdates();
return rootWorkInProgress;
}
// 开始工作循环
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
// 以fiber为工作单元
function performUnitOfWork(initOfWork) {
const current = unitOfWork.alternate;
// 开始工作
const next = beginWork(current, unitOfWork);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// 没有子节点了就完成这个单元
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
}
```
[fiberTree](img/3.fiberTree.jpeg)
```js
// 1. 第一次beginWork => updateHostRoot
// 1.1 处理更新队列 拿出 {element}
// 1.2 协调children reconcileChildren root是肯定有current的
// 1.2.1 element是一个对象
的虚拟dom
return workInProgress.child;
}
// 第二次的时候是
dom原生标签
function updateHostComponent(current, workInProgress) {
const type = workInProgress.type; // div
const nextProps = workInProgress.pendingProps;
let nextChildren = nextProps.children; // props.children 拿到虚拟dom节点
const isDirectTextChild = shouldSetTextContent(type, nextProps);
// 如果孩子是一个文本就不创建fiber节点了
if (isDirectTextChild) nextChildren = null;
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
// 第三次 hello 文本节点
function updateHostText() {
// 不做处理 直接完成
return null;
}
function reconcileChildren() {
if (current) {
reconcileChildFibers();
} else {
mountChildFibers();
}
}
```
#### reconcileChildFibers
```js
// 协调children
// dom-diff
function reconcileChildFibers(returnFiber, currentFirstChild, newChild) {
// 协调孩子
if (typeof newChild === "object" && newChild !== null) {
// 1. 第一次root节点进来的时候 children是一个对象 element: {{$$typeof: REACT_ELEMENT_TYPE}} 对应的虚拟dom节点
// return了新创建的fiber节点
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE: {
// react元素
return placeSingleChild(
reconcileSingleElement(
returnFiber,
currentFirstChild,
newChild,
lanes
)
);
}
}
// 2. 第二次的children是一个数组 ['hello', world]
if (isArray(newChild)) {
return reconcileChildrenArray(
returnFiber,
currentFirstChild,
newChild,
lanes
);
}
}
// 3. 第三次 'hello'
if (typeof newChild === "string") {
// 创建fiber的子节点 createFiberFromText
return reconcileSingleTextNode(reconcileSingleTextNode());
}
// 4. 第四次 span的children是纯文本 child = null
return null;
}
```
##### reconcileSingleElement
```js
// 协调单个元素 创建fiber节点
function reconcileSingleElement(returnFiber, currentFirstChild, element) {
// 创建fiber返回
const created = createFiberFromElement(element, returnFiber.mode, lanes);
created.return = returnFiber;
return created;
}
```
##### createFiberFromElement
```js
// tag的处理
function createFiberFromElement() {
const fiber = createFiberFromTypeAndProps(
type,
key,
pendingProps,
mode,
lanes
);
return fiber;
}
function createFiberFromTypeAndProps(type, key, pendingProps, mode, lanes) {
// 开始是不知道类型的
let fiberTag = IndeterminateComponent;
if (typeof type === "function") {
} else if (typeof type === "string") {
// div span 原生组件
fiberTag = HostComponent;
}
const fiber = createFiber(fiberTag, pendingProps, key, mode);
return fiber;
}
```
##### placeSingleChild
```js
// 放置child
function placeSingleChild(newFiber) {
// 根节点的root的current是存在的 增加了flags
if (shouldTrackSideEffects && newFiber.alternate === null) {
// 给fiber增加新增的flags
newFiber.flags |= Placement;
}
return newFiber;
}
```
##### reconcileChildrenArray
```js
// dom-diff 初次渲染就是创建fiber 构建fiber链条 return sibling
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren) {
let resultingFirstChild = null;
let newIdx = 0;
for (; newIdx < newChildren.length; newIdx++) {
// 1. 创建fiber
const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
// 2. 放置fiber newFiber.index = newIndex;
placeChild(newFiber, lastPlacedIndex, newIdx);
// 3. 构建fiber链表
if (previousNewFiber === null) {
// 大儿子
resultingFirstChild = newFiber;
} else {
// 弟弟 构建fiber的链条
previousNewFiber.sibling = newFiber;
}
// 父fiber
previousNewFiber = newFiber;
}
return resultingFirstChild;
}
```
##### createChild
```js
// 创建child的fiber
function createChild() {
// 文本
const created = createFiberFromText();
// 对象
const created = createFiberFromElement();
created.return = returnFiber;
return created;
}
```
##### placeChild
```js
function placeChild() {
newFiber.index = newIndex;
}
```
#### completeUnitOfWork 完成工作
```js
function completeUnitOfWork(unitOfWork) {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
let next;
// 完成了 执行fiber的完成工作
// 如果是原生的就创建真实的dom节点
// next = completeWork(current, completedWork, renderLanes);
next = completeWork(current, completedWork);
if (next !== null) {
workInProgress = next;
return;
}
// 如果这个节点return null 找弟弟
// 上一个文本节点处理完成了 处理弟弟 对应的fiber节点
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
workInProgress = siblingFiber;
return;
}
// 如果弟弟也完成了 就表示让parent完成 同时找叔叔开始工作
completedWork = returnFiber;
// 这个很重要
workInProgress = completedWork;
} while (completedWork !== null);
// 更改状态 并发模型下的退出状态
if (workInProgressRootExitStatus === RootInProgress) {
workInProgressRootExitStatus = RootCompleted; // 完成 commitRoot()
}
}
```
#### completeWork 完成 fiber
```js
// ReactFiberCompleteWork
// 创建真实的dom节点
// 和beginWork一样也是判断tag
function completeWork(current, workInProgress) {
switch (workInProgress.tag) {
case HostText: {
// 第一个完成的是文本节点 创建真实的dom节点
// document.createTextNode(text)
workInProgress.stateNode = createTextInstance();
// 向上冒泡副作用 以前是收集副作用链条 effectList
// 将儿子的副作用和自己的副作用全部上交 全部冒泡到根节点 统一处理
bubbleProperties(workInProgress);
return null;
}
case HostComponent: {
// 原生dom节点
// 1. 创建真实的dom节点 document.createElement(type)
const instance = createInstance();
// 2. 将自己的孩子全部挂到自己的身上 parent.appendChild(child);
appendAllChildren(instance, workInProgress, false, false);
// 3. 处理dom属性
finalizeInitialChildren();
// 4. 上交副作用
bubbleProperties(workInProgress);
return null;
}
case HostRoot: {
// 根节点的完成
bubbleProperties(workInProgress);
return null;
}
}
}
```
##### bubbleProperties 上交副作用
```js
function bubbleProperties(completedWork) {
let subtreeFlags = NoFlags;
// 拿到完成的fiber的儿子
let child = completedWork.child;
// 遍历当前fiber的所有子节点
while (child !== null) {
// 将child的副作用合并到上面
subtreeFlags |= child.subtreeFlags; // child的child的副作用上交
subtreeFlags |= child.flags; // child的flag
child = child.sibling;
}
// 全部收集
completedWork.subtreeFlags |= subtreeFlags;
}
```
#### commitRoot 提交阶段
```js
function commitRoot() {
// 如果有副作用
if (subtreeHasEffects || rootHasEffect) {
// before mutation
const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects();
// mutation阶段
commitMutationEffects(root, finishedWork, lanes);
}
}
function commitMutationEffects(root, finishedWork) {
commitMutationEffectsOnFiber(finishedWork, root);
}
// 又是根据不同的tag做处理
function commitMutationEffectsOnFiber(finishedWork, root) {
switch (finishedWork.tag) {
case HostRoot:
// 递归提交 先处理child
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
// 在处理自己的
commitReconciliationEffects(finishedWork);
return;
case HostText: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
return;
}
default:
break;
}
}
// 先递归处理children
function recursivelyTraverseMutationEffects(root, parentFiber) {
if (parentFiber.subtreeFlags & MutationMask) {
let child = parentFiber.child;
while (child !== null) {
// 递归处理children
commitMutationEffectsOnFiber(child, root, lanes);
child = child.sibling;
}
}
}
// 在处理自己的
function commitReconciliationEffects(finishedWork) {
const flags = finishedWork.flags;
// 不同的副作用执行不同的操作
if (flags & Placement) {
// 插入操作 初次渲染的是插入操作
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
}
}
```
##### commitPlacement
```js
function commitPlacement(finishedWork) {
// 找到host的fiber 找到真实的dom节点
const parentFiber = getHostParentFiber(finishedWork);
switch (parentFiber.tag) {
case HostComponent: {
break;
}
case HostRoot: {
// div#root
const parent = parentFiber.stateNode.containerInfo;
// 获取最近的弟弟dom节点
const before = getHostSibling(finishedWork);
// 插入或者追加一个
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
break;
}
default:
break;
}
}
// 向上找到真实的dom节点
function getHostParentFiber(fiber) {
let parent = fiber.return;
while (parent !== null) {
// hostComponent hostRoot
if (isHostParent(parent)) {
return parent;
}
parent = parent.return;
}
}
function insertOrAppendPlacementNodeIntoContainer(node, before, parent) {
const { tag } = node;
const isHost = tag === HostComponent || tag === HostText;
if (isHost) {
const stateNode = node.stateNode;
if (before) {
// insertBefore
insertInContainerBefore(parent, stateNode, before);
} else {
// appendChild
appendChildToContainer(parent, stateNode);
}
}
}
```
##### getHostSibling
[getHostSibling](img/4.getHostSibling.jpeg)
```js
// 找到参考点
// 需要是真实存在的dom节点
// 1. 找弟弟 如果弟弟有不是新增的(可以作为参考点)
// 2. 没有弟弟就找 return (parent) 找return的弟弟
// 3. 不能是新插入的节点 在真实的dom中不存在 不能作为插入点
function getHostSibling(fiber) {
let node = fiber;
// 给while循环取名 siblings
siblings: while (true) {
while (node.sibling === null) {
if (node.return === null || isHostParent(node.return)) {
// 没有before的插入点
return null;
}
// 没有弟弟就找return
node = node.return;
}
node.sibling.return = node.return;
// 找弟弟
node = node.sibling;
// 如果不是原生节点
while (node.tag !== HostComponent && node.tag !== HostRoot) {
// 如果是一个新的需要插入的就找弟弟 插入的不是真实存在的dom
if (node.flags & Placement) {
continue siblings;
}
if (node.child === null) {
continue siblings;
} else {
node.child.return = node;
node = node.child;
}
}
// 不是插入的就表示是真实存在的节点
if (!(node.flags & Placement)) {
return node.stateNode;
}
}
}
```
## 组件
```js
// 函数组件 类组件(很少使用)
// 在beginWork中如果类型是函数
// renderWithHooks
```
### 函数组件
```js
// FunctionComponent
// 1. 在root节点beginWork之后返回的是一个函数组件
// 2. 在beginWork的时候判断tag的类型为 未知的组件 IndeterminateComponent进行挂载
function mountIndeterminateComponent() {
const props = workInProgress.pendingProps;
// return Component() 就是执行函数组件返回
value = renderWithHook();
if (isClassComponent) {
workInProgress.tag = ClassComponent;
} else {
// 函数组件
workInProgress.tag = FunctionComponent;
reconcileChildren(null, workInProgress, value, renderLanes);
return workInProgress.child;
}
}
function renderWithHooks() {
// TODO: 执行hooks的逻辑
let children = Component(props, secondArg);
return children;
}
// 3. 执行函数组件拿到 jsx 虚拟dom节点之后 继续 reconcileChildren
// 4. 完成节点 当函数组件的fiber节点都完成之后 函数组件也要完成
bubbleProperties(workInProgress);
// 5. 提交阶段
recursivelyTraverseMutationEffects(root, finishedWork);
commitReconciliationEffects(finishedWork);
// 6. 在函数组件提交自己的副作用的时候 placeSingleChild的时候有一个flags 执行 commitPlacement 操作
// 执行插入操作
function insertOrAppendPlacementNodeIntoContainer() {
if (isHost) {
} else {
// 组件 找到真实的dom节点插入
const child = node.child;
if (child !== null) {
insertOrAppendPlacementNodeIntoContainer(child, before, parent);
let sibling = child.sibling;
while (sibling !== null) {
insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
sibling = sibling.sibling;
}
}
}
}
```
### 类组件
```js
// 类组件使用已经很少了 不考虑 setState的更新过程了
// 只考虑初次渲染的逻辑
// 1. 实现 React.Component()
// ReactBaseClasses
function Component(props, context, updater) {
this.props = props;
this.context = context;
this.refs = {};
// 更新器
this.updater = updater;
}
// 区分函数组件
Component.prototype.isReactComponent = {};
// setState方法
Component.prototype.setState = function (partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, "setState");
};
// 纯组件
pureComponentPrototype.isPureReactComponent = true;
// 2. beginWork阶段 在root处理children的时候
function createFiberFromTypeAndProps() {
if (isClassComponent) {
// 函数组件的时候修改了类型
fiberTag = ClassComponent;
}
}
// 3. 组件在beginWork的时候执行的就是
function updateClassComponent() {
const instance = workInProgress.stateNode;
if (instance === null) {
// 这个很重要 给updater赋值classComponentUpdater
constructClassInstance();
mountClassInstance();
} else {
// 更新
// updateClassInstance()
}
// 主要就是执行render函数得到child 然后 reconcile
const nextUnitOfWork = finishClassComponent();
return nextUnitOfWork;
}
function constructClassInstance() {
let instance = new ctor(props, context);
adoptClassInstance(workInProgress, instance);
return instance;
}
// 更新器
function adoptClassInstance() {
instance.updater = classComponentUpdater;
workInProgress.stateNode = instance;
}
// 4. 挂载类组件
// ReactFiberClassComponent.js
function mountClassInstance() {
// 更新队列
initializeUpdateQueue(workInProgress);
// 处理content
// 处理生命周期函数
}
// 5. 返回unitOfWork()
function finishClassComponent() {
const instance = workInProgress.stateNode;
let nextChildren;
// 执行render函数得到返回值的虚拟dom节点 然后递归处理孩子
nextChildren = instance.render();
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
workInProgress.memoizedState = instance.state;
return workInProgress.child;
}
// 6. 完成阶段
bubbleProperties(workInProgress);
// 7. 提交阶段
recursivelyTraverseMutationEffects(root, finishedWork);
commitReconciliationEffects(finishedWork);
```
#### setState
```js
// 1. Component上的方法 调用的是更新器上的方法
Component.prototype.setState = function (partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, "setState");
};
// 2. 在mount的过程中
// updateClassComponent => constructClassInstance => adoptClassInstance
function adoptClassInstance() {
instance.updater = classComponentUpdater;
}
// 3. 更新器
const classComponentUpdater = {
enqueueSetState() {
// 1. 创建更新
const update = createUpdate(eventTime, lane);
update.payload = payload;
// 2. 入队
const root = enqueueUpdate(fiber, update, lane);
// 3. 调度 setState最后也是执行的 schedule
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
},
};
// ReactFiberClassUpdateQueue
function enqueueUpdate() {
if (isUnsafeClassRenderPhaseUpdate()) {
// 构建更新的环形链表
return unsafe_markUpdateLaneFromFiberToRoot();
} else {
// 现在都是 concurrent的 先添加到数组中在preStack的时候在构建成循环链表
return enqueueConcurrentClassUpdate();
}
}
```
## 事件系统
```js
// 更新依赖事件系统 先了解react的事件系统
// 1. 注册
// 2. 监听 addEventListener
// 3. 触发 dispatchEvent 合成事件 事件优先级
// react的事件是基于插件来实现的
// 合成事件 为什么需要合成 性能高 处理浏览器兼容性 SyntheticEvent
// 插件系统 new Set() 5个插件往里面加事件
// react17对事件进行很大的改动
```
### 事件流
[eventUse](img/5.eventuse.jpeg)
```js
// 事件流 捕获 => 目标 => 冒泡
// 事件委托 事件代理 利用时间冒泡实现
// 事件注册 通过插件实现 代理到目标容器上 之前是代理在 document 文档对象上的
// 事件绑定 element.addEventListener(event, function, useCapture)
// 事件触发 dispatchEvent
```
### 事件注册
```js
// 在 createRoot的过程中会进行事件的注册
// 监听只有一次的
function createRoot(container) {
listenToAllSupportedEvents(container);
}
// 1. 通过插件来进行注册
// 1.1 记录click和onClick的映射关系
// 1.2 给allNativeEvents添加事件
SimpleEventPlugin.registerEvents();
// events事件相关的
// react-dom-bindings/src/events/DOMPluginEventSystem
function listenToAllSupportedEvents(container) {
// 遍历事件进行注册 通过插件系统给 allNativeEvents 赋值
allNativeEvents.forEach((domEventName) => {
// 2. 进行事件的监听
listenToNativeEvent(domEventName, false, rootContainerElement);
listenToNativeEvent(domEventName, true, rootContainerElement);
});
}
// 2 监听事件 拿到事件去监听
function listenToNativeEvent() {
// addTrappedEventListener()
// 2.1 拿到listener 事件是有优先级的概念的
// click对应的是离散事件 dispatchDiscreteEvent
let listener = createEventListenerWrapperWithPriority();
// 2.2 绑定监听 捕获和冒泡阶段 就是 target.addEventListener
addEventCaptureListener();
addEventBubbleListener();
}
```
### 插件系统
```js
// react的事件是基于插件来实现的 一共5个事件插件
// ChangeEventPlugin SelectEventPlugin EnterLeaveEventPlugin BeforeInputEventPlugin
// SimpleEventPlugin click等事件
// 插件就是往allNativeEvents中添加数据 然后进行监听
const allNativeEvents = new Set();
```
#### 简单事件插件
```js
SimpleEventPlugin.registerEvents(); // registerSimpleEvents
// 定义一些列的简单事件 将这些添加到allNativeEvents中
const simpleEventPluginEvents = ["click"];
// 用来记录原生click和react事件onClick的映射关系
const topLevelEventsToReactNames = new Map();
// 用来记录onClick和[click]的关系 一个react事件可能是由多个事件的 比如onChange就对应多个事件
const registrationNameDependencies = {};
// DOMEventProperties
function registerSimpleEvents() {
for (let i = 0; i < simpleEventPluginEvents.length; i++) {
const eventName = simpleEventPluginEvents[i]; // click
const domEventName = eventName.toLowerCase(); // click
const capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1); // Click
// 注册两个阶段的处理函数
// registerSimpleEvent(domEventName, "on" + capitalizedEvent); // click => onClick
// 1. 使用一个map来记录原生事件和react事件的映射关系
// 触发了click事件我们要拿到对应的props.onClick事件执行
topLevelEventsToReactNames.set(domEventName, reactName);
// 2. 注册两阶段的事件 注册捕获和冒泡的事件 就是往 allNativeEvents 中添加数据
// registerTwoPhaseEvent(reactName, [domEventName]);
// dependencies: [domEventName]
registrationNameDependencies[registrationName] = dependencies;
for (let i = 0; i < dependencies.length; i++) {
allNativeEvents.add(dependencies[i]);
}
}
}
```
### 事件派发
```js
// dispatchEvent触发事件就是拿到对应的listener然后去执行
// 当createRoot的时候会初始化事件系统 然后不同的插件注册不同的事件
// 然后将事件都注册在target容器上(之前是委托给document) target.addEventListener()
// 不同的事件有不同的优先级 暂时先不考虑
// click对应的是离散 dispatchDiscreteEvent 当我们触发这些事件的时候就会进行事件的派发
// 不同的事件对应不同的事件优先级 也对应不同的处理函数
// 不同的事件是优先级不同 最后还是执行 dispatchEvent函数
function dispatchDiscreteEvent() {
setCurrentUpdatePriority(); // 设置当前的优先级
dispatchEvent();
// 执行完之后还原上次的优先级
}
function dispatchEvent() {
// dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay()
// 就是找到最近的fiber节点
return_targetInst = getClosestInstanceFromNode(nativeEventTarget);
const blockedOn = findInstanceBlockingEvent();
// 为插件系统派发事件
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
return_targetInst, // 作为参数传递
targetContainer
);
}
function dispatchEventForPluginEventSystem() {
// 合成事件中的事件都是批量执行的
// executionContext |= BatchedContext;
batchedUpdates(() =>
dispatchEventsForPlugins(
domEventName,
eventSystemFlags,
nativeEvent,
ancestorInst,
targetContainer
);
);
}
```
#### dispatchEventsForPlugins
```js
// 最后还是回到了插件系统上
// 为插件系统派发事件
function dispatchEventsForPlugins() {
const dispatchQueue = [];
// 1. 提取事件 给dispatchQueue 赋值
// 插件去实现的 SimpleEventPlugin.extractEvents
extractEvents();
// 2. 处理事件 拿到事件按顺序执行 捕获和冒泡顺序是不一样的
processDispatchQueue();
}
```
##### extractEvents
```js
// 提取事件
function extractEvents() {
// 不同的事件对应不同的合成事件
let SyntheticEventCtor = SyntheticEvent;
// click对应的是鼠标合成事件
SyntheticEventCtor = SyntheticMouseEvent;
// 累加单个阶段的监听
const listeners = accumulateSinglePhaseListeners();
if (listeners.length > 0) {
const event = new SyntheticMouseEvent();
// 如果有监听函数就添加到派发队列中
dispatchQueue.push({ event, listeners });
}
}
function accumulateSinglePhaseListeners() {
let instance = targetFiber;
let listeners = [];
// 递归向上去找处理函数 只能是原生的节点
while (instance !== null) {
// props.click 如果找到这个属性
const listener = getListener(instance, reactEventName);
listeners.push(
createDispatchListener(instance, listener, lastHostComponent)
);
instance = instance.return;
}
}
// 1. 虚拟dom节点 element.props
// 2. 在创建fiber的时候 fiber.memProps = props
// 3. 在完成阶段 createInstance的时候 updateFiberProps(domElement, props)
// node["__reactProps$" + randomKey] = props
// 4. 这里拿到的就是虚拟dom节点上的props 里面就可以拿到我们定义的 onClick回调函数了
function getListener() {
// node["__reactProps$" + randomKey] = props
// 同理在完成阶段的 createInstance的时候会进行赋值
const props = getFiberCurrentPropsFromNode(stateNode);
// props.onClick
const listener = props[registrationName];
// 处理disabled的
// shouldPreventMouseEvent()
return listener;
}
```
##### processDispatchQueue
```js
// 处理事件 遍历去执行
function processDispatchQueue() {
for (let i = dispatchListeners.length - 1; i >= 0; i--) {
const { instance, currentTarget, listener } = dispatchListeners[i];
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
// listener(event);
executeDispatch(event, listener, currentTarget);
previousInstance = instance;
}
}
// 合成事件实例的 currentTarget 是在不断变化的
// event nativeTarget 是原始的事件元是不回变化的
// currentTarget 是当前的事件元 是不断变化的 指向当前的 target
function executeDispatch(event, listener, currentTarget) {
event.currentTarget = currentTarget;
// invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
listener && listener(event);
event.currentTarget = null;
}
```
#### findInstanceBlockingEvent
```js
function findInstanceBlockingEvent() {
let targetInst = getClosestInstanceFromNode(nativeEventTarget);
return_targetInst = targetInst;
}
const internalInstanceKey = "__reactFiber$" + randomKey;
function getClosestInstanceFromNode() {
return targetNode[internalInstanceKey];
}
// 合成加上的这个属性 在完成阶段 createInstance 的时候 会提前缓存fiber节点到真实dom上
function createInstance() {
// node["__reactFiber$" + randomKey] = hostInst;
precacheFiberNode(internalInstanceHandle, domElement);
// 还会更新fiber的props 之后找props.onClick就可以快速的找到
// node["__reactProps$" + randomKey] = props
updateFiberProps(domElement, props);
}
```
#### 合成事件
```js
// SyntheticEvent
// 我们在回调中获取到的event不是原生的event对象是react合成的对象
// 为什么需要合成事件 性能高 屏蔽浏览器差异
// 不同事件对应不同的事件对象
// 不同的合成事件对应的接口属性不一样
const MouseEventInterface = {
...UIEventInterface,
clientX: 0,
clientY: 0,
};
// 通过不同的接口创建不同的合成事件 click对应的是鼠标合成事件
const SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface);
function createSyntheticEvent(Interface) {
// 基类 拷贝属性
function SyntheticBaseEvent() {
for (const propName in Interface) {
this[propName] = nativeEvent[propName];
}
return this;
}
Object.assign(SyntheticBaseEvent.prototype, {
preventDefault() {}
stopPropagation() {}
})
return SyntheticBaseEvent
}
```
## 类组件状态变更 setState
```js
// 1. 点击button触发事件系统执行回调函数
// 2. 调用this.setState函数
// 3. 执行Component原型上的方法
Component.prototype.setState = function () {
this.updater.enqueueSetState();
};
// 4. 执行更新器的方法
// update是在何时赋值的? 组件在beginWork的过程中 如果是类组件
function updateClassComponent() {
constructClassInstance();
// instance.updater = classComponentUpdater;
}
// 5. 跟新器
const classComponentUpdater = {
enqueueSetState() {
// 5.1 创建更新
createUpdate();
// 5.2 更新入队
enqueueUpdate();
// 5.3 开始调度更新
scheduleUpdateOnFiber();
},
};
// 6. 调度 最后的最后都要回到这个调度来
// click事件的优先级是 1 SyncLane
scheduleSyncCallback(performSyncWorkOnRoot);
// 然后执行 workLoop performUnitOfWork 这个时候current就不是null了
function beginWork(current, workInProgress, renderLanes) {
if (current !== null) {
}
// 判断tag
// 类组件
updateClassComponent();
}
// 7. 更新类组件
function updateClassComponent() {
if (instance === null) {
// 初次挂载
mountClassInstance();
} else {
// 更新 更新state状态
updateClassInstance();
}
const nextUnitOfWork = finishClassComponent();
return nextUnitOfWork;
}
// ReactFiberClassComponent
function updateClassInstance() {
cloneUpdateQueue(current, workInProgress);
// 计算新的state
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
// context上下文
// 还有一些生命周期
instance.props = newProps;
instance.state = newState;
}
function finishClassComponent() {
// 执行render得到新的虚拟dom 然后协调children
nextChildren = instance.render();
reconcileChildren();
}
// 8. 类组件协调新的child 开心下一个unit的工作
// updateHostComponent 更新原生dom节点
// reconcileChildren 执行的就是 reconcileChildFibers了 初次挂载是mountChildFibers
// 更新阶段就需要加到副作用标识了
// 更新 dom-diff
// 1. beginWork阶段的处理
// 1.1 第一个更新的是root节点 返回child就是我们定义的class类组件
cloneChildFibers(current, workInProgress);
// 1.2 classComponent开始工作
function updateClassComponent() {
if (instance === null) {
// 初次挂载 initializeUpdateQueue 初始化更新队列
mountClassInstance();
} else {
// 更新
// cloneUpdateQueue
// processUpdateQueue 处理更新队列 更新state this.setState添加的update
updateClassInstance();
}
// 执行render函数返回虚拟dom节点 reconcileChildren
return finishClassComponent();
}
// 1.3 classComponent协调子节点的时候是div原生dom节点 返回这个fiber节点
placeSingleChild(reconcileSingleElement());
function reconcileSingleElement() {
let child = currentFirstChild;
if (child) {
// 更新阶段child是存在的
// const clone = createWorkInProgress(fiber, pendingProps);
const existing = useFiber(child);
existing.return = returnFiber;
return existing;
}
// 初次挂载阶段创建一个fiber返回
const created = createFiberFromElement(element, returnFiber.mode, lanes);
created.return = returnFiber;
return created;
}
// 设计到dom-diff的比较 位置的移动 需要判断index
function placeSingleChild() {
const current = newFiber.alternate;
if (current !== null) {
// 判断位置 如果是移动的节点
newFiber.flags |= Placement;
} else {
newFiber.flags |= Placement;
}
}
// 1.4 div节点 原生dom节点 协调子节点
function updateHostComponent() {
reconcileChildren();
}
// 1.5 children是一个数组 [p, button]
// dom-diff就在这里
function reconcileChildrenArray() {
let oldFiber = currentFirstChild;
// lodFiber存在 更新的阶段 和初次挂载的基本是一样的
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
const newFiber = updateSlot();
placeChild();
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
// 初次挂载就是循环创建fiber然后构建fiber链条即可
if (oldFiber === null) {
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = createChild(); // 创建
placeChild(); // 放置
// 构建fiber链条
if (previousNewFiber === null) {
// 大儿子
resultingFirstChild = newFiber;
} else {
// 弟弟 构建fiber的链条
previousNewFiber.sibling = newFiber;
}
// 父fiber
previousNewFiber = newFiber;
}
}
// 返回大儿子 p
return resultingFirstChild;
}
// 更新
function updateSlot() {
if (typeof newChild === "string" || typeof newChild === "number") {
// 更新文本
return updateTextNode();
}
if (typeof newChild === "object" && newChild !== null) {
// 更新元素
return updateElement();
}
}
function updateTextNode() {
// 新增阶段
if (current === null) {
createFiberFromText();
// createFiberFromElement()
} else {
// 更新
const existing = useFiber();
}
}
// 1.6 返回 p的节点继续工作 还是原生节点 但是child是一个文本 nextChildren = null
// 开始完成这个节点
function updateHostComponent() {
const isDirectTextChild = shouldSetTextContent();
if (isDirectTextChild) nextChildren = null;
reconcileChildren();
}
// 1.7 p节点完成后就返回 sibling button
// 然后完成button
// button完成之后完成div节点
// 在完成classComponent
// 再完成root节点
// 都完成直接就进行commit提交
// 2. completed阶段的处理
// 2.1 在p节点工作的时候 child是一个文本children为null 然后完整这个 p节点
function completeWork() {
switch (workInProgress.tag) {
// p阶段完成的时候是原生dom节点
case HostComponent: {
if (current !== null) {
// 更新操作 workInProgress.updateQueue = updatePayload;
// diffProperties 比对属性
// 如果有更新就标记为update markUpdate workInProgress.flags |= Update;
updateHostComponent(current, workInProgress, type, newProps);
} else {
// 初次挂载
const instance = createInstance();
appendAllChildren();
// 初始化dom属性
finalizeInitialChildren();
}
// 上下冒泡副作用
bubbleProperties(workInProgress);
return null;
}
// 原生dom节点是判断是否有updatePayload 如果是文本节点就判断新旧文本是否相等
case HostText: {
if (current !== null) {
// if(oldText !== newText) markUpdate(workInProgress)
updateHostText();
}
}
}
}
// 有更新就增加了副作用标识
function updateHostComponent() {
// diffProperties 收集 updatePayload 这里是没有更新的
const updatePayload = prepareUpdate();
workInProgress.updateQueue = updatePayload;
// 如果有更新就标记为 更新
if (updatePayload) {
// workInProgress.flags |= Update;
markUpdate(workInProgress);
}
}
// 2.2 在完成阶段将副作用上交给 div
// 2.3 div完成的时候将副作用上交给classComponent
// 2.4 类组件再将自己的副作用和div上交的副作用一起都给root
// root节点完成之后就开始提交
// 3. 提交阶段的处理
// workLoop
function commitRoot() {
// 有副作用
if (subtreeHasEffects || rootHasEffect) {
commitMutationEffects(root, finishedWork, lanes);
}
}
// 3.1 提交阶段 开始是root节点
// 有两个副作用标识的
// 一个是针对children的 subtreeFlags
// 一个是自己的 flags
// ReactFiberCommitWork 又是各种递归操作
function commitMutationEffects() {
// commitMutationEffectsOnFiber()
switch (finishedWork.tag) {
case HostRoot: {
// 先递归处理child的 subtreeFlags
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
// 在处理自己的 flags 好像只处理的 Placement
commitReconciliationEffects(finishedWork);
// 处理更新的
if (flags & Update) {
}
}
}
}
// 递归处理child 就是上交上来的 subtreeFlags 不在收集effectList副作用链表了
function recursivelyTraverseMutationEffects() {
// 处理的是 subtreeFlags
if (parentFiber.subtreeFlags & MutationMask) {
let child = parentFiber.child;
while (child !== null) {
// 递归处理children
commitMutationEffectsOnFiber(child, root, lanes);
child = child.sibling;
}
}
}
// 处理自己的
function commitReconciliationEffects(finishedWork) {
const flags = finishedWork.flags;
if (flags & Placement) {
commitPlacement(finishedWork); // 插入操作
finishedWork.flags &= ~Placement;
}
}
// 3.2 root节点递归处理classComponent的副作用
// 也是递归处理 到div HostComponent 同理也是先递归处理child
// 先处理p然后再是span(没有副作用的不处理)
// 处理p
// p的副作用标识为: flags 4 subTreeFlags 0
function commitMutationEffectsOnFiber() {
switch (finishedWork.tag) {
case HostComponent: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
// 处理更新
if (flags & Update) {
// 在 完成阶段 updateHostComponent 收集的更新
const updatePayload = finishedWork.updateQueue;
// 提交更新
commitUpdate();
}
}
}
}
// 3.3 提交更新
function commitUpdate() {
// 更新dom属性 updateDOMProperties()
// children要单独处理文本的问题 setTextContent
updateProperties(domElement, updatePayload, type, oldProps, newProps);
// 更新fiber的属性
updateFiberProps(domElement, newProps);
}
```
## hooks
[renderWithHooks](img/7.renderWithHooks.png)
```js
// react通过一个全局变量来管理 ReactCurrentDispatcher
// React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
const ReactCurrentDispatcher = { current: null };
const dispatcher = ReactCurrentDispatcher.current;
// react包中的ReactHooks文件导出相应的方法
function useState() {
// hook通过 ReactCurrentDispatcher 来赋值
return dispatcher.useState();
}
function useReducer() {
return dispatcher.useReducer();
}
// 在beginWork的过程中 函数组件我们使用的是renderWithHooks
// 具体的逻辑是在 react-reconciler 包中的 ReactFiberHooks
function renderWithHooks() {
// hooks的逻辑就是在这里
// 给dispatch赋值
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
}
// 挂载的时候
const HooksDispatcherOnMount = {
useState: mountState,
useReducer: mountReducer,
};
// 更新的时候
const HooksDispatcherOnUpdate = {
useState: updateState,
useReducer: updateReducer,
};
```
### useReducer
#### mountReducer
[mountReducer](img/8.mountReducer.jpeg)
```js
// useState是useReducer的语法糖
function renderWithHooks() {
// 给hooks赋值
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
// 在 renderWithHooks 的过程中会执行Component 会调用函数组件执行
// 会执行 React.useReducer() 函数
let children = Component(props, secondArg);
}
// mount挂载阶段 支持第三个参数的
function mountReducer(reducer, initialArg, init) {
// 构建hook的单向链表
const hook = mountWorkInProgressHook();
hook.memoizedState = hook.baseState = initialArg; // hook的状态
hook.queue = {}; // hook的更新队列
// 返回的更新函数
const dispatch = (queue.dispatch = dispatchReducerAction);
return [hook.memoizedState, dispatch];
}
// dispatch派发函数
function dispatchReducerAction(fiber, queue, action) {
console.log("dispatchReducerAction");
}
```
#### updateReducer
[updateReducer](img/9.updateReducer.png)
```js
// 点击按钮的时候就是执行绑定的dispatch方法
function dispatchReducerAction() {
// 更新入队
const root = enqueueConcurrentHookUpdate();
// 开始调度 都是回归到了这个方法
scheduleUpdateOnFiber();
}
// 在beginWork阶段更新函数组件 重新执行 Component()
function updateFunctionComponent() {
// 执行renderWithHooks
nextChildren = renderWithHooks();
reconcileChildren();
}
// 再次执行 React.useReducer() 更新阶段了
function updateReducer(reducer, initialArg, init) {
// 获取新的hook
const hook = updateWorkInProgressHook();
// 更新队列
const queue = hook.queue;
const pendingQueue = queue.pending;
// 获取老的hook
const current = currentHook;
let newState = current.memoizedState;
if (pendingQueue !== null) {
queue.pending = null;
// 第一个更新
const first = pendingQueue.next;
let update = first;
do {
// 计算新的状态
const action = update.action;
newState = reducer(newState, action);
update = update.next;
} while (update !== null && update !== first);
}
hook.memoizedState = newState;
return [hook.memoizedState, hook.dispatch];
}
// 在进行dom-diff比较 更新页面
// commitRoot() 提交变更
```
#### update
```js
// 更新阶段
// root => functionComponent => div
// 1. beginWork阶段
// reconcileSingleElement 进行 dom-diff的比对
// 然后函数组件 执行 renderWithHooks 重新执行函数 然后重新执行 useReducer()
function updateFunctionComponent() {}
// updateReducer
function updateReducer() {}
// 2. completeWork 完成阶段
if (current !== null && workInProgress.stateNode != null) {
// 更新阶段
updateHostComponent();
}
function updateHostComponent() {
// 1. 收集payload ['id', 'newId', 'children': '2']
const updatePayload = prepareUpdate();
// 2. 标记为更新
workInProgress.flags |= Update;
}
// 3. 提交阶段
// 3.1 递归处理children
// 3.2 处理自己的placement
// 3.3 Update 提交上面的payload
if (flags & Update) {
commitUpdate();
// // 更新dom属性 文本children的单独处理
// updateProperties(domElement, updatePayload, type, oldProps, newProps);
// // 更新fiber的属性
// updateFiberProps(domElement, newProps);
}
```
### useState
```js
// useState是useReducer的语法糖
// 内置了reducer的useReducer
// 多个的setState应该是合并的 批量更新的
function mountState() {
const hook = mountWorkInProgressHook;
const queue = {};
const dispatch = (queue.dispatch = dispatchSetState.bind());
return [hook.memoizedState, dispatch];
}
// 派发函数
function dispatchSetState(fiber, queue, action) {
const update = {};
const root = enqueueConcurrentHookUpdate();
// 调度
scheduleUpdateOnFiber(root, fiber);
}
// 更新 调用 updateReducer
function basicStateReducer(state, action) {
return typeof action === "function" ? action(state) : action;
}
function updateState(initialState) {
return updateReducer(basicStateReducer, initialState);
}
```
### useEffect
[eventLoop](img/13.eventLoop.jpeg)
[useEffect](img/14.useEffect.jpeg)
```js
// 副作用操作 dom操作 接口调用
// 初次挂载
function mountEffect(create, deps) {
// 和useLayout的逻辑是一样的 区别在于执行的时间不一样 对应的是fiber的flags不一样
// PassiveEffect Passive hook的标识是Passive 8
return mountEffectImpl(PassiveEffect, HookPassive, create, deps);
}
function mountEffectImpl() {
const hook = updateWorkInProgressHook();
// 依赖
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
currentlyRenderingFiber.flags |= fiberFlags;
// fiber的 memoizedState 指向的是hook的单向链表
// useState的 memoizedState 指向的是 内部的状态
// hook的 memoizedState 指向一个 effect的循环链表
// fiber的updateQueue的componentUpdateQueue的lastEffect指向effect的state的循环链表的最后一个effect
// effect为什么要单独创建一个循环链表??? 遍历 effect更方便 不需要遍历单向的effect链表
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
undefined,
nextDeps
);
}
// 构建更新的循环链表
function pushEffect() {
const effect = {};
let componentUpdateQueue = currentlyRenderingFiber.updateQueue;
if (componentUpdateQueue === null) {
// 创建一个函数组件的更新队列 {lastEffect: null}
componentUpdateQueue = createFunctionComponentUpdateQueue();
} else {
}
return effect;
}
// 在提交阶段 执行副作用
function commitRoot() {
// 如果自己有 Passive 或者child有Passive 表示有需要执行的
// 在下一次的调度中 执行 effect的钩子
if (
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
(finishedWork.flags & PassiveMask) !== NoFlags
) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
// 添加到调度中 NormalSchedulerPriority 优先级
scheduleCallback(NormalSchedulerPriority, () => {
flushPassiveEffects();
return null;
});
}
}
if (subtreeHasEffects || rootHasEffect) {
// 1. before mutation dom变更前
// const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects();
// 2. 提交阶段
commitMutationEffects(root, finishedWork, lanes);
// 3. dom变更之后
commitLayoutEffects(finishedWork, root, lanes);
}
}
// effect的执行
function flushPassiveEffects() {
// 先执行 unMount
commitPassiveUnmountEffects();
// 再执行mount
commitPassiveMountEffects();
}
// 两个阶段的处理是一样的 都是先递归然后处理执行的
// 处理函数也是拿到循环的更新队列去执行
function commitPassiveMountEffects() {
// commitPassiveMountOnFiber
// 判断类型
switch (finishedWork.tag) {
// 函数组件
case FunctionComponent: {
// 先递归
recursivelyTraversePassiveMountEffects();
if (flags & Passive) {
commitHookPassiveMountEffects();
}
}
}
}
function recursivelyTraversePassiveMountEffects() {
while (child !== null) {
commitPassiveMountOnFiber(
root,
child,
committedLanes,
committedTransitions
);
child = child.sibling;
}
}
// 执行hook
// commitHookEffectListMount
function commitHookEffectListMount(flags, finishedWork) {
const updateQueue = finishedWork.updateQueue;
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
// 循环链表
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
if ((effect.tag & flags) === flags) {
// Mount
const create = effect.create;
// 在unmount的时候就是执行 destroy函数
effect.destroy = create();
}
effect = effect.next;
} while (effect !== null && effect !== firstEffect);
}
}
// 更新阶段
function updateEffect(create, deps) {
return updateEffectImpl(PassiveEffect, HookPassive, create, deps);
}
function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
// 如果依赖没变就不需要执行 直接return
if (currentHook !== null) {
const prevEffect = currentHook.memoizedState;
destroy = prevEffect.destroy;
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 还是要push到里面 维持链表
hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
return;
}
}
}
// 如果依赖没变就不添加
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
destroy,
nextDeps
);
}
```
### useLayoutEffect
```js
// 和useEffect类型 执行的时机不一样
function mountLayoutEffect(create, deps) {
// Update hook的标识是 Layout 4
return mountEffectImpl(UpdateEffect, HookLayout, create, deps);
}
// 更新
function updateLayoutEffect(create, deps) {
return updateEffectImpl(UpdateEffect, HookLayout, create, deps);
}
// 在 commitLayoutEffects阶段 判断类型 如果是函数组件
// commitMutationEffectsOnFiber
function commitMutationEffectsOnFiber() {
switch (finishedWork.tag) {
case FunctionComponent: {
recursivelyTraverseMutationEffects(root, finishedWork);
commitReconciliationEffects(finishedWork);
if (flags & Update) {
// 执行 Unmount 的钩子
commitHookEffectListUnmount();
}
}
}
}
// 之后在提交effect和useEffect的是类似的
// commitLayoutEffectOnFiber
function commitLayoutEffectOnFiber() {
// 也是判断tag
switch (finishedWork.tag) {
case FunctionComponent: {
// 也是先递归处理
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes
);
if (flags & Update) {
// 在提交自己的 commitHookEffectListMount();
commitHookLayoutEffects(finishedWork, HookLayout | HookHasEffect);
}
break;
}
}
}
// 递归的处理
function recursivelyTraverseLayoutEffects() {
while (child !== null) {
const current = child.alternate;
commitLayoutEffectOnFiber(root, current, child, lanes);
child = child.sibling;
}
}
```
### use
```js
```
### useRef
```js
```
### useContext
```js
```
## dom-diff
### 单节点的
[dom-diff](img/10.dom-diff.jpeg)
```js
// 1. 单节点的处理
function reconcileSingleElement() {
// (1) 判断是否有老的fiber
if (child !== null) {
// (2) 判断key
if (child.key === element.key) {
// (3) 判断type
if (child.elementType === element.type) {
// 都相同就表示复用
const existing = useFiber();
} else {
// type不一样
deleteRemainingChildren()
break
}
} else {
// 否则就删除
deleteChild();
}
}
// 然后创建新的
createFiberFromElement();
}
// 递归删掉
function deleteRemainingChildren() {
while (childToDelete !== null) {
deleteChild(returnFiber, childToDelete);
childToDelete = childToDelete.sibling;
}
}
// react18中会用一个 deletions 来存储要删除的
// 加上一个flags returnFiber.flags |= ChildDeletion
function deleteChild() {
returnFiber.deletions = [childToDelete];
// 有子节点要删除 在提交阶段要删掉
returnFiber.flags |= ChildDeletion;
}
// 在提交阶段
function recursivelyTraverseMutationEffects() {
for (let i = 0; i < deletions.length; i++) {
commitDeletionEffects()
}
}
function commitDeletionEffects() {
// 递归删除
recursivelyTraverseDeletionEffects()
// parentInstance.removeChild(child);
removeChildFromContainer()
}
// 2. 以前有多个的
```
### 多个节点的
[dom-diff](img/11.dom-diff.jpeg)
[dom-diff](img/12.dom-dif.jpeg)
```js
// dom-diff三个规则
// 1. 只对同级元素进行比较 不同层级的不比较
// 2. 不同的类型对应不同的元素
// 3. 通过key来标识同一个节点
// 三轮遍历
// (1) 第一轮遍历
// 如果key不同就直接结束
// newChildren 或 oldFiber 遍历完,结束本轮循环
// key相同type不同标记为删除 继续遍历
// key相同type相同就复用 继续遍历
// (2) 第二轮遍历
// newChildren 遍历完而 oldFiber 还有 就删除剩余的
// oldFiber完了 newChildren还有 就标记为插入
// 都遍历完成了 diff结束
// 都没有完成 就要进行 节点的移动
// (3) 第三轮遍历
// 处理节点移动的情况
// 1. 将剩下的老的fiber放入一个map existingChildren
// 2. 声明一个 lastIndex用来记录最后一个不需要移动的来节点
// 3. 循环剩下的虚拟dom节点 在map中找key和type相同的节点
// 4. 如果能找到就复用将老的fiber从map中删除 判断lastIndex和index的关系
// 如果index < lastIndex 不需要移动老的fiber
// 否则需要移动老的fiber 更新lastIndex变量
// 5. 找不到就创建洗你的fiber节点
// 6. 如果虚拟dom循环借宿了 map中还有就将剩下的全部标记为删除
function reconcileChildrenArray() {
// 1. 第一轮循环
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
// 试图去更新
const newFiber = updateSlot();
// 放置child
lastPlacedIndex = placeChild();
// 构建链表关系
}
// 2. 第二轮遍历
// 2.1 如果老的有多的就需要删除
if (newIdx === newChildren.length) {
deleteRemainingChildren();
}
// 2.2 如果新的有多的就需要插入操作
// 初次挂载的逻辑也是在这里的
if (oldFiber === null) {
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = createChild();
placeChild();
}
}
// 3. 第三轮遍历 需要处理移动的情况
// 3.1 老的维护为一个map
const existingChildren = mapRemainingChildren(returnFiber, oldFiber);
// 3.2 遍历在里面找
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = updateFromMap();
lastPlacedIndex = placeChild();
}
// 3.3 如果老的还有就删除
if (shouldTrackSideEffects) {
existingChildren.forEach((child) => deleteChild(returnFiber, child));
}
}
```
## schedule 调度
```js
// schedule是一个单独的模块
// lanes 模型的优先级是 react的概念
// lanes优先级 变成事件优先级
// 然后都要变成调度的优先级 给 schedule模块进行调度
```
### 最小堆 SchedulerMinHeap
[minHeap](img/15.minHeadp.jpeg)
```js
// https://github.com/facebook/react/blob/main/packages/scheduler/src/SchedulerMinHeap.js
// 使用数组实现最小堆 找到最高优先级的任务
// peek() 查看堆的顶点
// pop() 弹出堆的定点后需要调用siftDown函数向下调整堆
// push() 添加新节点后需要调用siftUp函数向上调整堆
// siftDown() 向下调整堆结构, 保证最小堆
// siftUp() 需要向上调整堆结构, 保证最小堆
// 左子节点索引=(父节点索引+1)2-1 (0+1)2-1=1
// 右子节点索引=左子节点索引+1
// 父节点索引=(子节点索引-1)/2 (1-1)/2=0
```
### scheduleCallback
[messageChannel](img/16.messageChannel.jpeg)
[scheduleCallback](img/17.scheduleCallback.jpeg)
[scheduleCallback](img/18.scheduleCallback.jpeg)
```js
// 在 react-reconciler中我们会调用 scheduler包中的调度函数
scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
// 之前使用 requestIdleCallback 简单的实现
function unstable_scheduleCallback(callback) {
requestIdleCallback(callback);
}
// 但是存在兼容性问题 而且时间是不确定的
// react和浏览器是合作式的调度 每帧申请5ms的时间片
// react中使用 MessageChannel来模拟
function scheduleCallback(priorityLevel, callback, options) {
// 1. 任务的开始时间
let currentTime = getCurrentTime();
// 2. 超时时间 不同的优先级对应不同的超时时间
let timeout;
switch (priorityLevel) {
default:
timeout = NORMAL_PRIORITY_TIMEOUT;
break;
}
// 3. 过期时间 作为优先级的排序依据
let expirationTime = startTime + timeout;
const newTask = { id: taskIdCounter++, callback, sortIndex: expirationTime };
// 4. 将任务添加到最小堆数组中
push(taskQueue, newTask);
// 5. 开始调度任务 schedulePerformWorkUntilDeadline
requestHostCallback(flushWork);
}
// 用 MessageChannel
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = performWorkUntilDeadline;
// 发送消息 执行 flushWork
function requestHostCallback() {
port.postMessage(null);
}
// 开始执行任务队列中的任务
function flushWork() {
// return workLoop()
// 1. 取出堆顶元素
currentTask = peek(taskQueue);
while (currentTask !== null) {
const callback = currentTask.callback;
// 2. 执行任务函数 如果返回了一个函数就需要继续执行
// 这里的cb就是 performConcurrentWorkOnRoot fiber树的构建是比较耗时的
const continuationCallback = callback(didUserCallbackTimeout);
if (typeof continuationCallback === "function") {
currentTask.callback = continuationCallback;
return true;
} else {
// 任务完成了就弹出这个任务
pop(taskQueue);
}
}
}
```
### lane 优先级
[lane](img/19.lane.jpeg)
```js
// 31个车道 16个优先级 车道就是优先级 是react的概念
// 会转为调度模块中的优先级
// react之前有四个优先级
// 事件 任务 更新 schedule
// 现在是三个优先级
// 1.1 react里面的lane优先级 16个优先级 ReactFiberLane react中有很多个任务对应不同的车道
// 1.2. 事件优先级 lanesToEventPriority 将lane变成事件优先级 ReactEventPriorities
// 一共4种 事件优先级也是车道 做了归并
// 车道有31个通过 lanesToEventPriority变成event的优先级
const DiscreteEventPriority = SyncLane; // 离散
const ContinuousEventPriority = InputContinuousLane; // 连续
const DefaultEventPriority = DefaultLane; // 默认事件优先级
const IdleEventPriority = IdleLane; // 空闲
// 2. scheduler模块中的 SchedulerPriorities 五个优先级
const ImmediatePriority = 1;
const UserBlockingPriority = 2;
const NormalPriority = 3;
const LowPriority = 4;
const IdlePriority = 5;
// 优先级是相互独立的 可以相互转化的
// 将lane转成scheduler中的优先级(存在对应关系的)
// 调度模块5个优先级 每个优先对应不同的超时时间
```
### 更新优先级
```js
// 每个更新会有一个优先级 每个任务存在优先级 一些低优先级的任务可能会被跳过
// 1. 在处理化更新队列的时候 在queue中添加一些属性来记录
function initializeUpdateQueue(fiber) {
const queue = {
baseState: fiber.memoizedState, // 跳过前的state
firstBaseUpdate: null, // 单向链表 第一个被跳过的更新
lastBaseUpdate: null, // 跳过的链表尾 之后后将两个链表做合并
shared: {
// shared是一个循环链表
pending: null,
lanes: NoLanes,
},
};
fiber.updateQueue = queue;
}
// 2. 在处理更新的时候会判断优先级
function processUpdateQueue() {
// 2.1 要将 pending的循环链表拆开和上次跳过的链表合并
const queue = workInProgress.updateQueue;
// 上次跳过的老的更新链表头
let firstBaseUpdate = queue.firstBaseUpdate;
let lastBaseUpdate = queue.lastBaseUpdate;
// 新的更新的循环链表
let pendingQueue = queue.shared.pending;
// 如果有新的跟新
if (pendingQueue !== null) {
queue.shared.pending = null;
const lastPendingUpdate = pendingQueue;
// 第一个更新
const firstPendingUpdate = lastPendingUpdate.next;
// 变成单向链表
lastPendingUpdate.next = null;
// 将两个链表合并
if (lastBaseUpdate === null) {
// 没有老链表
firstBaseUpdate = firstPendingUpdate;
} else {
// 有老的链表就将老的链表尾指向新的链表头 两个链表就变成了一个
lastBaseUpdate.next = firstPendingUpdate;
}
// 指向新的链表尾 两个链表就合并为一个了
lastBaseUpdate = lastPendingUpdate;
}
// 2.2 链表不为空 有更新 进行跟新
if (firstBaseUpdate !== null) {
// 上次跳过跟新的state状态
let newState = queue.baseState;
let newFirstBaseUpdate = null;
let newLastBaseUpdate = null;
let update = firstBaseUpdate;
do {
// 2.3 判断是否要跳过更新
if (!isSubsetOfLanes(renderLanes, update.lane)) {
// 2.3.1 优先级不够就要跳过更新
const clone = {};
if (newLastBaseUpdate === null) {
// 都指向第一次跳过的更新
newFirstBaseUpdate = newLastBaseUpdate = clone;
// 跳过的时候的状态值
newBaseState = newState;
} else {
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
} else {
// 2.3.2 优先级够的时候就要计算新的状态
// 但是如果已经有跳过的更新了 还是要加到链表上的 保证执行的顺序
if (newLastBaseUpdate !== null) {
const clone = {};
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// 2.4 计算新的状态
newState = getStateFromUpdate();
}
update = update.next;
if (update === null) {
break;
}
} while (true);
// 赋值
if (newLastBaseUpdate === null) newBaseState = newState;
workInProgress.memoizedState = newState;
}
}
```
### 渲染
[scheduleUpdateOnFiber](img/20.scheduleUpdateOnFiber.jpeg)
```js
function updateContainer() {
// 1. 在 updateContainer 的时候获取lane
const lane = requestUpdateLane(current);
// 2. 在创建更新的时候加入lane const update = { lane, payload: null, next: null }
const update = createUpdate(lane);
// 3. 入队更新 使用并发的模式 添加到concurrentQueues 数组中
const root = enqueueUpdate(current, update, lane);
// 4. 在调度的时候也要加入lane
scheduleUpdateOnFiber(root, current, lane, eventTime);
}
function markRootUpdated(root, lane, eventTime) {
// 1. 标记根节点上的更新 根节点上有一个更新
// root.pendingLanes |= updateLane;
markRootUpdated(root, lane, eventTime);
ensureRootIsScheduled();
}
function ensureRootIsScheduled() {
// 2. 回去最高优先级的
const nextLanes = getNextLanes();
const newCallbackPriority = getHighestPriorityLane(nextLanes);
let newCallbackNode;
if (includesSyncLane()) {
// 同步 更新的时候就到了这里
// 1. 添加到同步队列中
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
// 2. flushSyncCallbacks 刷新清空同步队列
Scheduler_scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);
} else {
//
let schedulerPriorityLevel;
// 3. 将lane变成事件优先级
let eventPriority = lanesToEventPriority(nextLanes);
switch (eventPriority) {
// 4. 将事件优先级变成调度优先级
case DefaultEventPriority:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
}
// 开始调度 返回的是一个newTask
newCallbackNode = Scheduler_scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
}
root.callbackNode = newCallbackNode;
}
```
#### 并发渲染
```js
// 在跟上执行并发渲染
function performConcurrentWorkOnRoot() {
// 获取根节点上的任务 在 ensureRootIsScheduled中赋值的
const originalCallbackNode = root.callbackNode;
// 拿到最高lanes
let lanes = getNextLanes();
// 判断是否要时间分片
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
// 说明任务还没有完成 提交阶段会清空
if (root.callbackNode === originalCallbackNode) {
// 下次继续干
return performConcurrentWorkOnRoot.bind(null, root);
}
}
// 初次渲染是同步的 renderRootSync
// 在 createRoot 的时候我们传递一个参数
const root = createRoot(document.getElementById("root"), {
// 可以传递参数 这是一个开关
unstable_concurrentUpdatesByDefault: true,
});
// 这样 fiber.mode |= ConcurrentUpdatesByDefaultMode;
// 是否需要时间分片
const shouldTimeSlice =
// 没有阻塞的车道 默认的任务是阻塞的 可以通过开关来控制
!includesBlockingLane(root, lanes) &&
// 不包含过期的
!includesExpiredLane(root, lanes) &&
// disableSchedulerTimeoutInWorkLoop = false
(disableSchedulerTimeoutInWorkLoop || !didTimeout);
// 并发
function renderRootConcurrent(root, lane) {
workLoopConcurrent();
// 还没有完成表示要继续工作
if (workInProgress !== null) {
return RootInProgress;
} else {
// 在complete阶段完成之后修改为5 表示完成了
return workInProgressRootExitStatus;
}
}
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
// 测试并发渲染
sleep(6);
performUnitOfWork(workInProgress);
}
}
// 完成阶段
function completeUnitOfWork() {
// do {}while
// 这里就是fiber树构建完成了 修改状态
workInProgressRootExitStatus = RootCompleted;
}
// 在提交阶段
function commitRoot() {
root.callbackNode = null;
}
```
#### 同步渲染
```js
function performSyncWorkOnRoot() {
let lanes = getNextLanes(root, NoLanes);
renderRootSync(root, lanes);
}
function renderRootSync(root, lanes) {
// 1. 渲染优先级不一样就准备fiber栈
// 本次渲染的lanes workInProgressRootRenderLanes = renderLanes = lanes;
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
prepareFreshStack(root, lanes);
}
workLoop();
}
// lanes传下去 在处理更新队列的时候就可以获取到 优先级了
// workLoop => performUnitOfWork => beginWork
// => updateHost() => processUpdateQueue()
```
#### 更新阶段
```js
// 点击按钮触发更新 在合成事件中 会派发 dispatchEvent
function dispatchDiscreteEvent() {
const previousPriority = getCurrentUpdatePriority();
// 设置当前的优先级
setCurrentUpdatePriority(DiscreteEventPriority);
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
setCurrentUpdatePriority(previousPriority);
}
// 这个时候就是执行同步的操作
// 1. 添加到同步队列中
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
// 2. flushSyncCallbacks 刷新清空同步队列
Scheduler_scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);
```
### 批量更新
```js
// 如果任务的优先级一样就直接return
function ensureRootIsScheduled() {
const newCallbackPriority = getHighestPriorityLane(nextLanes);
const existingCallbackPriority = root.callbackPriority;
// 优先级一样就直接return了
if (existingCallbackPriority === newCallbackPriority) {
return;
}
root.callbackPriority = newCallbackPriority;
}
```
### 高优任务打断低优先级任务
```js
function ensureRootIsScheduled() {
if (existingCallbackNode !== null) {
// 取消任务 高优先级的任务会打断低优先级的任务 但是这个任务还是需要执行的
Scheduler_cancelCallback(existingCallbackNode);
}
}
```
### 饥饿问题
```js
// 在react中处理的 scheduler模块是不负责处理的
// 低优任务一只被打断 到过期了 异步马上变成同步的
// markStarvedLanesAsExpired()
```