这篇主要就是介绍render的一个流程,只到调度为止,并无深刻到每一个点,涉及到的数据结构我会在下一篇专门列出来,这里只要知道建立了什么数据结构就能够了,我主要是按16.12.0这个版本讲的,今天发现已经更新到16.13.0了不过具体改了什么还没看react
export function render(element, container, callback) { // validate container return legacyRenderSubtreeIntoContainer( null, element, container, false, callback ); }
function legacyRenderSubtreeIntoContainer( parentComponent, children, container, forceHydrate, callback ) { let root = container._reactRootContainer; let fiberRoot; if (!root) { // ReactDOMBlockingRoot实例,属性_internalRoot上挂载着fiberRootNode root = container._reactRootContainer = legacyCreateRootFromDOMContainer( container, forceHydrate ); fiberRoot = root._internalRoot; unbatchedUpdates(() => { updateContainer(children, fiberRoot, parentComponent, callback); }); } }
到这里是一个极简化的render,没有进入任何分支,下面咱们看下ReactDOMBlockingRoot实例的建立,以及挂载在该实例下的FiberRoot数据结构
function legacyCreateRootFromDOMContainer(container, forceHydrate) { // 通常Hydrate的状况是在服务端渲染和预渲染(prerender-spa-plugin) const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); if (!shouldHydrate) { let rootSibling; while ((rootSibling = container.lastChild)) { container.removeChild(rootSibling); } } return createLegacyRoot( container, shouldHydrate ? { hydrate: true } : undefined ); }
export function createLegacyRoot(container, options = {}) { return new ReactDOMBlockingRoot(container, LegacyRoot, options); }
class ReactDOMBlockingRoot { // container:domElement, tag:rootType, options: {hydrate:boolean} constructor(container, tag, options) { this._internalRoot = createRootImpl(container, tag, options); } }
function createRootImpl(container, tag, options) { const hydrate = options !== null && options.hydrate === true; const hydrationCallbacks = (options != null && options.hydrationOptions) || null; // 拿到fiberRootNode const root = createContainer(container, tag, hydrate, hydrationCallbacks); // 讲fiberNode 挂载到container对象上 markContainerAsRoot(root.current, container); return root; }
export function createContainer(container, tag, hydrate, hydrationCallbacks) { return createFiberRoot(container, tag, hydrate, hydrationCallbacks); }
export function createFiberRoot(container, tag, hydrate, hydrationCallbacks) { // 建立fiber树root节点 const root = new FiberRootNode(container, tag, hydrate); const uninitializedFiber = createHostRootFiber(tag); root.current = uninitializedFiber; uninitializedFiber.stateNode = root; initializeUpdateQueue(uninitializedFiber); return root; }
接下来是render的建立完fiberRoot后的另一个分支unbatchedUpdatesdom
const NoContext = /* */ 0b000000; const BatchedContext = /* */ 0b000001; const EventContext = /* */ 0b000010; const DiscreteEventContext = /* */ 0b000100; const LegacyUnbatchedContext = /* */ 0b001000; const RenderContext = /* */ 0b010000; const CommitContext = /* */ 0b100000; let executionContext = NoContext; export function unbatchedUpdates(fn, a) { const prevExecutionContext = executionContext; // 去除executionContext上的BatchedContext executionContext &= ~BatchedContext; // 往executionContext上添加LegacyUnbatchedContext executionContext |= LegacyUnbatchedContext; // 进行回调 try { return fn(a); } finally { executionContext = prevExecutionContext; if (executionContext === NoContext) { // 刷新同步任务队列 flushSyncCallbackQueue(); } } }
/** * * @param {*} element render的第一个参数 * @param {*} fiberRoot fiberRoot * @param {*} parentComponent 第一次渲染为null * @param {*} callback render的第三个参数,一个回调 */ export function updateContainer(element, fiberRoot, parentComponent, callback) { const current = fiberRoot.current; // 这里获得的是到目前为止 react还能处理多少单位时间(1单位时间是10ms) const currentTime = requestCurrentTimeForUpdate(); const suspenseConfig = requestCurrentSuspenseConfig(); // 计算过时时间,主要用在concurrent模式时使用 const expirationTime = computeExpirationForFiber( currentTime, current, suspenseConfig ); // 建立一个更新链表 const update = createUpdate(expirationTime, suspenseConfig); update.payload = { element }; // 处理回调函数 callback = callback === undefined ? null : callback; // 把建立的update添加到fiber的updateQueue上面 enqueueUpdate(current, update); // 进入调度 scheduleWork(current, expirationTime); return expirationTime; }
感受写的很差,太难写了,可是,看在我辛苦的份上动动小手点个赞哈哈,3q函数