注:本文应用的版本是React-17.0.0.3,开启enableNewReconciler = true,即会应用new.js后缀的文件;
文章专一于hook原理和react的渲染局部,波及到classComponent的局部一律略过;
mode = concurrent
本文所有代码都是基于以下react代码
import { React } from "../CONST";import { useEffect, useState } from "../react/packages/react";function FunctionComponent({name}) { const [count, setCount] = useState(0); const [subtraction, setSubTraction] = useState(1); useEffect(() => { setSubTraction(10); }, []); return ( <div className="function border"> {name} {count} {count1} <button onClick={() => setCount(pre => pre + 1)}>click</button> </div> );}const jsx = ( <div className="box border"> <p>start debugger</p> <FunctionComponent name="函数组件" /> </div>);ReactDOM.createRoot( document.getElementById('root')).render(jsx);
自顶向下介绍React
对于concurrent模式和Fiber架构等常识请看《React技术揭秘》
fiber的构造
function FiberNode( tag: WorkTag, // fiberTag pendingProps: mixed, // 组件参数/属性 key: null | string, // key mode: TypeOfMode, // 批示是lagecy还是conCurrent模式) { // Instance this.tag = tag; // fiber类型 this.key = key; // 用来做diff算法 this.elementType = null; this.type = null; this.stateNode = null; // 该fiber关联的实在dom // Fiber this.return = null; // 父fiber this.child = null; // 第一个孩子fiber this.sibling = null; // 相邻的第一个兄弟fiber this.index = 0; this.ref = null; this.pendingProps = pendingProps; // 新的组件参数/属性 this.memoizedProps = null; // 曾经渲染在页面上的(旧的)组件参数/属性 this.updateQueue = null; // 一个环状链表,存储的是effect副作用造成的update对象 this.memoizedState = null; // 对于functionComponent来说,存储的是hook对象链表 this.dependencies = null; this.mode = mode; // Effects this.flags = NoFlags; // 本fiber effect造成的副作用 this.subtreeFlags = NoFlags; // 子fiber effect的副作用 this.deletions = null; this.lanes = NoLanes; //本fiber的update lanes this.childLanes = NoLanes; // 所有子fiber的update lanes this.alternate = null; 。。。}
fibertag用来标示该fiber是哪种组件类型的fiber--25种
export const FunctionComponent = 0;export const ClassComponent = 1;export const IndeterminateComponent = 2; // Before we know whether it is function or classexport const HostRoot = 3; // Root of a host tree. Could be nested inside another node.export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.export const HostComponent = 5;export const HostText = 6;export const Fragment = 7;export const Mode = 8;export const ContextConsumer = 9;export const ContextProvider = 10;export const ForwardRef = 11;....
fiber树和dom的比照
fiber树的生成过程
咱们从render开始看fiber树是如何一个一个生成的
export function createRoot( container: Container, options?: RootOptions,): RootType { return new ReactDOMRoot(container, options);}function ReactDOMRoot(container: Container, options: void | RootOptions) { this._internalRoot = createRootImpl(container, ConcurrentRoot, options);}function createRootImpl( container: Container, tag: RootTag, options: void | RootOptions,) { ... // 生成root const root = createContainer( container, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, ); ... return root;}ReactDOMRoot.prototype.render = ReactDOMLegacyRoot.prototype.render = function( children: ReactNodeList,): void { const root = this._internalRoot; ... updateContainer(children, root, null, null);};
ReactDOM.createRoot(document.getElementById('root'))会创立ReactDomRoot对象,该类会调用createRootImpl初始化fiberRoot对象。随后的render函数是挂在类原型上的,会调用updateContainer
export function updateContainer( element: ReactNodeList, container: OpaqueRoot, parentComponent: ?React$Component<any, any>, callback: ?Function,): Lane { const current = container.current; const eventTime = requestEventTime(); const lane = requestUpdateLane(current); const update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property // being called "element". update.payload = {element}; callback = callback === undefined ? null : callback; if (callback !== null) { update.callback = callback; } enqueueUpdate(current, update, lane); const root = scheduleUpdateOnFiber(current, lane, eventTime); return lane;}
显然该函数是为了给fiberRoot建设一个update,并且update.payload是element即jsx;enqueueUpdate是为了把update挂到fiber.updateQueue;最初调用了scheduleUpdateOnFiber
重点来了,scheduleUpdateOnFiber是调度函数的入口函数,从这里开始进行fiber树的结构以及update的解决;
export function scheduleUpdateOnFiber( fiber: Fiber, lane: Lane, eventTime: number,): FiberRoot | null { // 从以后fiber开始向上冒泡直到找到root节点,同时更新所有父节点的childLanes const root = markUpdateLaneFromFiberToRoot(fiber, lane); if (root === null) { return null; } // 标记root有一个待更新 markRootUpdated(root, lane, eventTime); if (lane === SyncLane) { if ( // Check if we're inside unbatchedUpdates (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering (executionContext & (RenderContext | CommitContext)) === NoContext ) { performSyncWorkOnRoot(root); } else { ensureRootIsScheduled(root, eventTime); } } else { // Schedule other updates after in case the callback is sync. ensureRootIsScheduled(root, eventTime); } return root;}
ensureRootIsScheduled函数是为了向调度堆里push一个回调函数,最初还是会调用performSyncWorkOnFiber/performConcurrentWorkOnFiber;