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

发布于:2025-07-15 ⋅ 阅读:(21) ⋅ 点赞:(0)

回顾:初始化创建完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 cases
	var 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 标记本次更新的 lane
	sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
	var alternate = sourceFiber.alternate;
	if (alternate !== null) {
		alternate.lanes = mergeLanes(alternate.lanes, lane);
	}

	// 2. 向上遍历父节点,沿途合并 childLanes
	var 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;
	}
}


网站公告

今日签到

点亮在社区的每一天
去签到