当前位置: 首页 > news >正文

React源码3:update、fiber.updateQueue对象数据结构和updateContainer()中enqueueUpdate()阶段

回顾:初始化创建完FiberRootNode和hostRootFiber后,调用render方法进行更新root。

updateContainer()主流程

  1. updateContainer(入口)
  2. 获取 current Fiber(HostRoot)
  3. 获取事件时间(eventTime)、优先级(eventLane)
  4. 处理 context(大部分情况为默认)
  5. 创建 update 对象(payload: element)
  6. 入队并调度(enqueueUpdate,使用创建的update和初始化获得的fiber.updateQueue)
  7. 返回 lane(实际情况返回值无变量接收,基本不使用返回值)

数据结构1:update对象属性及其属性值来源

update对象封装在createUpdate方法中,工厂函数返回一个创建的update对象。

有6个属性。

//全局变量
var UpdateState = 0;
//对update的属性赋值
var eventTime = requestEventTime();
var lane = requestUpdateLane(current$1);
var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property
// being called "element".//工厂函数,返回一个update对象
function createUpdate(eventTime, lane) {var update = {eventTime: eventTime,lane: lane,tag: UpdateState,payload: null,callback: null,next: null,};return update;
}
update.payload = {element: element,
};callback = callback === undefined ? null : callback;if (callback !== null) {{if (typeof callback !== 'function') {error('render(...): Expected the last optional `callback` argument to be a ' +'function. Instead received: %s.',callback);}}update.callback = callback;
}//使用requestEventTime返回eventTime对象;
function requestEventTime() {if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {// We're inside React, so it's fine to read the actual time.return now();} // We're not inside React, so we may be in the middle of a browser event.if (currentEventTime !== NoTimestamp) {// Use the same start time for all updates until we enter React again.return currentEventTime;} // This is the first update since React yielded. Compute a new start time.currentEventTime = now();return currentEventTime;
}//使用requestUpdateLane创建一个eventLane变量
function requestUpdateLane(fiber) {// Special casesvar mode = fiber.mode;if ((mode & ConcurrentMode) === NoMode) {return SyncLane;} else if ((executionContext & RenderContext) !== NoContext &&workInProgressRootRenderLanes !== NoLanes) {// This is a render phase update. These are not officially supported. The// old behavior is to give this the same "thread" (lanes) as// whatever is currently rendering. So if you call `setState` on a component// that happens later in the same render, it will flush. Ideally, we want to// remove the special case and treat them as if they came from an// interleaved event. Regardless, this pattern is not officially supported.// This behavior is only a fallback. The flag only exists until we can roll// out the setState warning, since existing code might accidentally rely on// the current behavior.return pickArbitraryLane(workInProgressRootRenderLanes);}var isTransition = requestCurrentTransition() !== NoTransition;if (isTransition) {if (ReactCurrentBatchConfig$3.transition !== null) {var transition = ReactCurrentBatchConfig$3.transition;if (!transition._updatedFibers) {transition._updatedFibers = new Set();}transition._updatedFibers.add(fiber);} // The algorithm for assigning an update to a lane should be stable for all// updates at the same priority within the same event. To do this, the// inputs to the algorithm must be the same.//// The trick we use is to cache the first of each of these inputs within an// event. Then reset the cached values once we can be sure the event is// over. Our heuristic for that is whenever we enter a concurrent work loop.if (currentEventTransitionLane === NoLane) {// All transitions within the same event are assigned the same lane.currentEventTransitionLane = claimNextTransitionLane();}return currentEventTransitionLane;} // Updates originating inside certain React methods, like flushSync, have// their priority set by tracking it with a context variable.//// The opaque type returned by the host config is internally a lane, so we can// use that directly.// TODO: Move this type conversion to the event priority module.var updateLane = getCurrentUpdatePriority();if (updateLane !== NoLane) {return updateLane;} // This update originated outside React. Ask the host environment for an// appropriate priority, based on the type of event.//// The opaque type returned by the host config is internally a lane, so we can// use that directly.// TODO: Move this type conversion to the event priority module.var eventLane = getCurrentEventPriority();return eventLane;
}

数据结构2:hostRootFiber.updateQueue属性

fiber.updateQueue属性在创建root的时候初始化(在类组件挂载使用该方法初始化),数据结构如下:

function initializeUpdateQueue(fiber) {var queue = {baseState: fiber.memoizedState,firstBaseUpdate: null,//单链表lastBaseUpdate: null,shared: {pending: null, //循环单项链表interleaved: null,//循环单项链表lanes: NoLanes //位掩码},effects: null};fiber.updateQueue = queue; //将创建的update对象赋值给fiber属性
} 

一、root.render()方法

输入变量:

  • children:组件
  • root:根容器节点

无返回变量。

ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render = function (children) {var root = this._internalRoot;if (root === null) {throw new Error('Cannot update an unmounted root.');}{if (typeof arguments[1] === 'function') {error('render(...): does not support the second callback argument. ' + 'To execute a side effect after rendering, declare it in a component body with useEffect().');} else if (isValidContainer(arguments[1])) {error('You passed a container to the second argument of root.render(...). ' + "You don't need to pass it again since you already passed it to create the root.");} else if (typeof arguments[1] !== 'undefined') {error('You passed a second argument to root.render(...) but it only accepts ' + 'one argument.');}var container = root.containerInfo;if (container.nodeType !== COMMENT_NODE) {var hostInstance = findHostInstanceWithNoPortals(root.current);if (hostInstance) {if (hostInstance.parentNode !== container) {error('render(...): It looks like the React-rendered content of the ' + 'root container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + "root.unmount() to empty a root's container.");}}}}updateContainer(children, root, null, null);
};

二、updateContainer()方法 

输入变量:

  • element:root.render()的children变量,存入update.payload属性里。
  • cotainer:等价于root
  • parentCompoen:为null
  • callback:为null

返回变量:

  • lane(实际使用中并没有变量接收lane返回值)
function updateContainer(element, container, parentComponent, callback) {{onScheduleRoot(container, element);}var current$1 = container.current;var eventTime = requestEventTime();var lane = requestUpdateLane(current$1);  {markRenderScheduled(lane);}var context = getContextForSubtree(parentComponent);if (container.context === null) {container.context = context;} else {container.pendingContext = context;}{if (isRendering && current !== null && !didWarnAboutNestedUpdates) {didWarnAboutNestedUpdates = true;error('Render methods should be a pure function of props and state; ' + 'triggering nested component updates from render is not allowed. ' + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + 'Check the render method of %s.', getComponentNameFromFiber(current) || 'Unknown');}}var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property// being called "element".update.payload = {element: element};callback = callback === undefined ? null : callback;if (callback !== null) {{if (typeof callback !== 'function') {error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback);}}update.callback = callback;}var root = enqueueUpdate(current$1, update, lane);if (root !== null) {scheduleUpdateOnFiber(root, current$1, lane, eventTime);entangleTransitions(root, current$1, lane);}return lane;
}

三、enqueueUpdate()

将创建的update对象添加到update。

输入变量:

  • fiber:container指向的current,也就是hostRootFiber
  • update:createUpdate创建的update对象
  • lane:var lane = requestUpdateLane(current$1) (具体方法见update对象)

返回变量:

  • 返回enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane)
function enqueueUpdate(fiber, update, lane) {var updateQueue = fiber.updateQueue;if (updateQueue === null) {// Only occurs if the fiber has been unmounted.return null;}var sharedQueue = updateQueue.shared;{if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) {error('An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.');didWarnUpdateInsideUpdate = true;}}if (isUnsafeClassRenderPhaseUpdate()) {// This is an unsafe render phase update. Add directly to the update// queue so we can process it immediately during the current render.var pending = sharedQueue.pending;if (pending === null) {// This is the first update. Create a circular list.update.next = update;} else {update.next = pending.next;pending.next = update;}sharedQueue.pending = update; // Update the childLanes even though we're most likely already rendering// this fiber. This is for backwards compatibility in the case where you// update a different component during render phase than the one that is// currently renderings (a pattern that is accompanied by a warning).return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);} else {return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);}
}

四、enqueueConcurrentClassUpdate()

输入变量:

  • fiber:container指向的current,也就是hostRootFiber
  • queue:enqueueUpdate()的sharedQueue变量,fiber.updateQueue的Shared属性。
  • update:createUpdate创建的update对象
  • lane:var lane = requestUpdateLane(current$1) (具体方法见update对象)
function enqueueConcurrentClassUpdate(fiber, queue, update, lane) {//取出当前队列的 interleaved 链表(循环链表的最后一个节点,或 null)。var interleaved = queue.interleaved;// 如果 interleaved 为空,说明这是本次渲染的第一个交错更新:// update.next = update;// 创建一个只有自己一个节点的循环链表。// pushConcurrentUpdateQueue(queue);// 把这个队列加入全局的 concurrentQueues,方便渲染结束时统一处理。// 如果 interleaved 不为空,说明已经有交错更新:// update.next = interleaved.next;// 新 update 指向链表头(第一个节点)。// interleaved.next = update;// 原链表尾(last)指向新 update,插入到链表头部。// 这样链表依然是循环的,且新 update 成为最后一个节点。if (interleaved === null) {update.next = update;pushConcurrentUpdateQueue(queue);} else {update.next = interleaved.next;interleaved.next = update;}//更新队列的 interleaved 指针,始终指向循环链表的最后一个节点。queue.interleaved = update;//追溯 fiber 到根节点,标记本次更新的优先级(lane),并返回根节点return markUpdateLaneFromFiberToRoot(fiber, lane);
}

五、pushConcurrentUpdateQueue()和markUpdateLaneFromeFiberToRoot()

markUpdateLaneFromeFiberToRoot():从当前 fiber(组件节点)向上遍历到根节点(HostRoot),沿途合并/标记本次更新的优先级(lane),并返回根节点(FiberRoot)。

输入变量:

  • sourceFiber:container指向的current,也就是hostRootFiber
  • lane:var lane = requestUpdateLane(current$1) (具体方法见update对象)
function mergeLanes(a, b) {return a | b;
}var concurrentQueues = null;
function pushConcurrentUpdateQueue(queue) {if (concurrentQueues === null) {concurrentQueues = [queue];} else {concurrentQueues.push(queue);}
}function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {// 1. 当前 fiber 标记本次更新的 lanesourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);var alternate = sourceFiber.alternate;if (alternate !== null) {alternate.lanes = mergeLanes(alternate.lanes, lane);}// 2. 向上遍历父节点,沿途合并 childLanesvar node = sourceFiber;var parent = sourceFiber.return;while (parent !== null) {parent.childLanes = mergeLanes(parent.childLanes, lane);alternate = parent.alternate;if (alternate !== null) {alternate.childLanes = mergeLanes(alternate.childLanes, lane);}node = parent;parent = parent.return;}// 3. 如果到达 HostRoot,返回根节点if (node.tag === HostRoot) {var root = node.stateNode;return root;} else {return null;}
}

http://www.lryc.cn/news/587595.html

相关文章:

  • 新手向:Python自动化办公批量重命名与整理文件系统
  • 理解:进程、线程、协程
  • LLM表征工程还有哪些值得做的地方
  • python的小学课外综合管理系统
  • 我对muduo的梳理以及AI的更改
  • MFC UI表格制作从专家到入门
  • LeetCode经典题解:206、两数之和(Two Sum)
  • 018 进程控制 —— 进程等待
  • 算法训练营day18 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先
  • B站自动回复工具(破解)
  • 项目一第一天
  • 苍穹外卖学习指南(java的一个项目)(老师能运行,但你不行,看这里!!)
  • priority_queue的使用和模拟实现以及仿函数
  • 《C++内存泄漏8大战场:Qt/MFC实战详解 + 面试高频陷阱破解》
  • MFC/C++语言怎么比较CString类型最后一个字符
  • 【Linux】Ubuntu22.04安装zabbix
  • HTTP 四种常见方法
  • 【论文阅读】Thinkless: LLM Learns When to Think
  • ubuntu(22.04)系统上安装 MuJoCo
  • MFC UI控件CheckBox从专家到小白
  • 桶排序算法深度剖析
  • FastAPI + gRPC 全栈实践:Windows 开发到 Ubuntu 部署全指南
  • flink 和 spark 架构的对比
  • idea删除的文件怎么找回
  • IDEA中使用Servlet,tomcat输出中文乱码
  • JMeter 连接与配置 ClickHouse 数据库
  • 递推预处理floor(log_2{n})
  • 【脚本系列】如何使用 Python 脚本对同一文件夹中表头相同的 Excel 文件进行合并
  • uniapp video视频全屏播放后退出,页面字体变大,样式混乱问题
  • 基于Spring Boot的生活用品电商网站的设计与实现