const ReactDom: Object = {
// 服务端渲染使用hydrate 和render惟一的区别是 第四个参数的区别
hydrate(
element: React$Element<any>,
container: DOMContainer,
callback: ?Function,
){
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
true,
callback,
);
},
render(
element: React$Element<any>, // reactELement
container: DOMContainer, // 要挂载到哪一个dom节点
callback: ?Function, //应用渲染结束后 调用callback
){
return legacyRenderSubtreeIntoContainer(
null, // parentComponent
element,
container,
false,
callback,
)
}
}
复制代码
// 渲染Dom Tree到挂载的container节点上
function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any,any>, children: ReactNodeList, container: DomContainer, forceHydrate: boolean, callback: ?function ){
// container 是咱们传入的dom节点 判断container 是否有_reactRootContainer属性
// 正常状况下刚开始写入组装的dom标签是不会有_reactRootContainer属性的 因此第一次渲染 根节点root是不存在的
let root: _ReactSyncRoot = (container._reactRootContainer: any);
let fiberRoot;
if(!root){
// 建立一个root对象并赋值container._reactRootContainer
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate
)
// 结合 ReactSyncRoot 函数 this._internalRoot = root; fiberRoot 为 createContainer函数返回结果
fiberRoot = root._internalRoot;
// Initial mount should not be batched. 不使用批量更新
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
}
return getPublicRootInstance(fiberRoot);
}
function getPublicRootInstance( container: OpaqueRoot, ): React$Component<any, any> | PublicInstance | null {
// containerFiber 这里是一个fiber对象
const containerFiber = container.current;
if (!containerFiber.child) {
return null;
}
switch (containerFiber.child.tag) {
case HostComponent:
return getPublicInstance(containerFiber.child.stateNode);
default:
return containerFiber.child.stateNode;
}
}
复制代码
function legacyCreateRootFromDOMContainer( container: DOMContainer, forceHydrate: boolean, ): _ReactSyncRoot {
// 是否须要复用老节点并和新渲染的节点合并
const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
if (!shouldHydrate) {
let rootSibling;
// 删除container 下的全部子节点
while((rootSibling = container.lastChild)){
container.removeChild(rootSibling);
}
}
return new ReactSyncRoot(
container,
LegacyRoot,
shouldHydrate ? {
hydrate: true,
}
: undefined,
)
}
复制代码
// 若是 forceHydrate为false 则在render的时候调用shouldHydrateDueToLegacyHeuristic方法
// ROOT_ATTRIBUTE_NAME : 'data-reactroot' 为老版本的服务端渲染 的常量
// 会在root节点下的第一个节点加上 data-reactroot 属性标识目前应用是否有服务端渲染
function shouldHydrateDueToLegacyHeuristic (container){
const rootElment = getReactRootElementInContainer(getReactRootElementInContainer);
return !!(
rootElement &&
rootElement.nodeType === ELEMENT_NODE &&
rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
);
}
// 若是container.nodeType是 DOCUMENT_NODE 则返回 document
// 不然返回第一个子节点 经过判断 是否返回document 来判断是否须要一个 Hydrate
function getReactRootElementInContainer (container:any) {
if(container.nodeType === DOCUMENT_NODE){
return container.documentElement;
}else{
return container.firstChild;
}
}
复制代码
function ReactSyncRoot ( container: DOMContainer, tag: RootTag, options: void | RootOptions, ) {
// Tag is either LegacyRoot or Concurrent Root
const hydrate = options != null && options.hydrate === true;
const hydrationCallbacks =
(options != null && options.hydrationOptions) || null;
// 建立了一个 fiberRoot 进行赋值
const root = createContainer(container, tag, hydrate, hydrationCallbacks);
this._internalRoot = root;
}
复制代码
function updateContainer ( element:ReactNodeList, container: OpaqueRoot, parentComponent: ?React$Component<any,any>, callback: ?Function, ) {
// container.current 对应一个fiber对象
const current = container.current;
const currentTime = requestCurrentTime();
const suspenseConfig = requestCurrentSuspenseConfig();
// expirationTime 很是重要 优先级任务更新 拥有复杂计算
const expiriationTime = computeExpiriiationForFiber(currentTime,current,suspenseConfig);
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
suspenseConfig,
callback,
);
}
复制代码
//
function updateContainerAtExpirationTime ( element: ReactNodeList, container: OpaqueRoot, parentComponent: ?React$Component<any, any>, expirationTime: ExpirationTime, suspenseConfig: null | SuspenseConfig, callback: ?Function, ) {
//
return scheduleRootUpdate(
current,
element,
expirationTime,
suspenseConfig,
callback,
);
}
复制代码
function scheduleRootUpdate( current: Fiber, element: ReactNodeList, expirationTime: ExpirationTime, suspenseConfig: null | SuspenseConfig, callback: ?Function, ) {
// 建立 update对象 用来标记react应用 须要更新的地点
const update = createUpdate(expiriationTime,suspenseConfig);
// 设置update属性 初次渲染
update.payload = {element};
// enqueueUpdate 把 update对象加入到fiber对象上对应的 update enqueue 里
// setState forceUpdate的时候都会调用
enqueueUpdate(current, update);
// 开始进行任务调度 任务优先级概念 在同一时间有不一样的任务优先级的任务在队列里,
// 则须要有一个任务调度器在里面按照优先级先执行优先级高的任务再执行任务优先级低的任务
scheduleWork(current, expirationTime);
return expirationTime;
}
复制代码
执行节点调和、任务调度的操做javascript
react-reconciler/src/ReactFiberRoot.js
react-reconciler/src/ReactFiber.js
react-reconciler/src/ReactUpdateQueue.jsjava
function createFiberRoot( containerInfo:any, tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, ): FiberRoot {
const root: FiberRoot = (new FiberRootNode(containerInfo,tag,hydrate) : any);
if (enableSuspenseCallback) {
root.hydrationCallbacks = hydrationCallbacks;
}
// root节点的fiber
// current 是 uninitializedFiber
// 每个react element节点都会对应一个fiber对象 所以每个fiber对象也会有一个树结构
// root.current 是这个fiber对象树结构的顶点
const uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
return root;
}
// FiberRoot 对象上有哪些信息 属性
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
// 对应root节点 对应的fiber对象
this.current = null;
// react dom上的root节点 render里接收的第二个参数
this.containerInfo = containerInfo;
// 只有在持久的更新中会用到 react-dom中不会被用到
this.pendingChildren = null;
this.pingCache = null;
this.finishedExpirationTime = NoWork;
// 用来记录一个更新渲染当中 完成了的渲染更新任务 由于在整个树当中会存在各类不一样的更新任务
// 每个更新渲染咱们都会先渲染优先级最高的任务 优先级最高的任务渲染完成以后 就会是一个finishedWork
// 标记在应用的root上 更新完以后咱们要吧应用输出到dom节点上面 输出的过程中就是读取finishedWork属性
this.finishedWork = null;
// suspense 在renderFunction里 throw一个 promise 任务会被挂起 以后渲染suspense 组件的callback
// 等到promise result以后就会把result以后的数据显示出来 timeoutHandle来帮助记录这个过程中超时的状况
this.timeoutHandle = noTimeout;
// 只有 ·renderSubtreeIntoContainer·时候 才会有用 顶层context对象
this.context = null;
this.pendingContext = null;
// 应用是否要和原来的dom节点进行合并的标志
this.hydrate = hydrate;
this.firstBatch = null;
this.callbackNode = null;
this.callbackExpirationTime = NoWork;
this.firstPendingTime = NoWork;
this.lastPendingTime = NoWork;
this.pingTime = NoWork;
if (enableSchedulerTracing) {
this.interactionThreadID = unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
if (enableSuspenseCallback) {
this.hydrationCallbacks = null;
}
}
复制代码
react-fiberRoot 会建立 一个fibernode
double buffer 参考连接 react-fiber 经过单向链表的数据结构把整个fiber树串联起来,而后提供一个高效、方便的遍历方式 react
图一中的 uninitializedFiber 是指图二的 RootFiber RootFiber 能够经过child 拿到整个app tree节点属性 在fiber tree的遍历过程中,只会记录第一个子节点A,其余的子节点都会被当作A的兄弟节点sibling存在。当找不到sibling和child的时候将再也不遍历typescript
// fiber对应一个被处理或者已经处理了的组件 一个组件能够有一个或者多个Fiber
export type Fiber = {|
// tag用于区分 fiber的不一样类型,标记不一样的组件类型
tag: WorkTag,
// ReactElement里面的key
key: null | string,
// ReactElement.type,也就是咱们调用`createElement`的第一个参数
elementType: any,
// 异步组件resolved以后返回的内容,通常是`function`或者`class`
type: any,
// 对应节点的实例 class Component 对应的是class Component实例,Dom节点就对应的Dom节点的实例。function Component没有实例 因此没有stateNode
stateNode: any,
return: Fiber | null,
// 单链表结构树结构.
child: Fiber | null,
sibling: Fiber | null,
index: number,
// The ref last used to attach this node.
// I'll avoid adding an owner field for prod and model that as functions.
ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,
// 在setState以后 pendingProps 里存的新的props memoizedProps存的老的props
pendingProps: any,
memoizedProps: any,
// 在setState或者forceUpdate 建立更新以后就会存在此队列里
updateQueue: UpdateQueue<any> | null,
memoizedState: any,
// Dependencies (contexts, events) for this fiber, if it has any
dependencies: Dependencies | null,
mode: TypeOfMode,
// Effect
// 标记最终dom节点要进行哪些更新的工具
// 标记是否执行组件生命周期的内容
effectTag: SideEffectTag,
nextEffect: Fiber | null,
// The first and last fiber with side-effect within this subtree. This allows
// us to reuse a slice of the linked list when we reuse the work done within
// this fiber.
firstEffect: Fiber | null,
lastEffect: Fiber | null,
// 当前任务调度产生的过时时间
expirationTime: ExpirationTime,
childExpirationTime: ExpirationTime,
// current <=> wokInprogress 在开始更新和 更新到dom节点上时候 进行状态交换 不须要每次更新都建立 新的对象
// 一个复制的过程来保持这个两个对象都存在 在react当中叫 double buffer
// fiber与workInProgress互相持有引用,把current指针指向workInProgress tree,丢掉旧的fiber tree。
// 旧fiber就做为新fiber更新的预留空间,达到复用fiber实例的目的。
alternate: Fiber | null,
actualDuration?: number,
actualStartTime?: number,
selfBaseDuration?: number,
treeBaseDuration?: number,
_debugID?: number,
_debugSource?: Source | null,
_debugOwner?: Fiber | null,
_debugIsCurrentlyTiming?: boolean,
_debugNeedsRemount?: boolean,
_debugHookTypes?: Array<HookType> | null,
|};
复制代码
function createUpdate ( expirationTime:ExpirationTime, suspenseConfig:null | SuspenseConfig, ): Update<*>{
let update:Update<*> = {
expirationTime,
suspenseConfig,
tag:UpdateState,
payload:null,
callback:null,
next:null,
nextEffect:null,
}
}
复制代码
export type Update<State> = {
/** 更新的过时时间 */
expirationTime: ExpirationTime,
/** Suspense组件参数配置 */
suspenseConfig: null | SuspenseConfig,
/** * 指定更新类型,值为如下几种 * export const UpdateState = 0; 更新state * export const ReplaceState = 1;替换state * export const ForceUpdate = 2;强制更新state * export const CaptureUpdate =3; * 在渲染的过程中若是出现渲染错误被捕获了, ErrorBoundary * 组件捕获渲染错误后经过 CaptureUpdate 状态 从新渲染ErrorBoundary内节点 */
tag: 0 | 1 | 2 | 3,
/** * 要更新的内容 * 例如 把整个element Dom Tree 节点 渲染到payload节点上 */
payload: any,
/** 对应回调 例如 setState接收的第一个参数 render都有 */
callback: (() => mixed) | null, /** * 指向下一个更新 * update是总体存放在updateQueue中,updateQueue是一个相似于单项链表的结构 * 每个update都有一个next,这个next指向下一个update,updateQueue会有一个 * firstUodate和lastUpdate,记录这个单项链表的开头和结尾,这中间的开头和结尾都 * 是经过next串联起来一一对应,把整个update 单链表的结构链接起来 * 在updateQueue中 先读取firstUodate对应的update,而后从第一个update的next查找下一个update,直到读取到lastUpdate为止 这就是updateQueue队列的执行顺序 */ next: Update<State> | null, /** 指向下一个更新 nextEffect: Update */ nextEffect: Update<State> | null, //DEV only priority?: ReactPriorityLevel, }; export type UpdateQueue<State> = { // 每次应用渲染更新完成以后 baseState记录最新state 在下次更新时候直接读取baseState // 下次计算更新直接在此基础上计算 而不是获取最初state baseState: State, // 队列里的第一个update firstUpdate: Update<State> | null, // 队列里的最后一个update lastUpdate: Update<State> | null, // 第一个捕获类型的 update firstCapturedUpdate: Update<State> | null, // 最后一个捕获类型的 update lastCapturedUpdate: Update<State> | null, firstEffect: Update<State> | null, lastEffect: Update<State> | null, firstCapturedEffect: Update<State> | null, lastCapturedEffect: Update<State> | null, }; 复制代码
// 建立或者更新fiber 上的update enqueue
// 先 判断 fiber 上alternate 是否有缓存的旧fiber 若是有 则建立两个队列
// 若是两个队列相同 或只有一个队列 则把update对象推入queue1中
// 若是两个队列不一样 则将update更新入两个queue中
function enqueueUpdate<State>( fiber:Fiber, update:Update<State> ){
// Update queues are created lazily.
const alternate = fiber.alternate;
let queue1;
let queue2;
if(alternate === null){
// 在这里理解为 alternate至关于 workInProgress ,fiber 至关于 current
// 若是alternate 不存在 则表明 这是第一次建立
// 第一次渲染 reactDom.render的时候执行
queue1 = fiber.updateQueue;
queue2 = null;
// 若是此时在初始化 没有updateQueue 则建立updateQueue
if(queue1 === null){
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState)
}
}else{
// There are two owners.
// workInProgress 存在记录 则表明建立过了
queue1 = fiber.updateQueue;
queue2 = alternate.updateQueue;
if(queue1 === null){
if(queue2 === null){
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
queue2 = alternate.updateQueue = createUpdateQueue(
alternate.memoizedState,
);
}else{
queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
}
}else{
if(queue2 === null){
queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
}
}
}
if(queue2 === null || queue1 === queue2 ){
// 初次渲染 只有一个queue
appendUpdateToQueue(queue1,update)
}else{
// 性能优化 避免重复的update
if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
appendUpdateToQueue(queue1, update);
appendUpdateToQueue(queue2, update);
}else{
// 两个队列的lastUpdate 都存在时候 他的lastUpdate 应该是相同的对象,因此 只用将update 推入 queue1
appendUpdateToQueue(queue1, update);
// 改变queue2 lastUpdate 的指针
queue2.lastUpdate = update;
}
}
}
复制代码
export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
const queue: UpdateQueue<State> = {
baseState,
firstUpdate: null,
lastUpdate: null,
firstCapturedUpdate: null,
lastCapturedUpdate: null,
firstEffect: null,
lastEffect: null,
firstCapturedEffect: null,
lastCapturedEffect: null,
};
return queue;
}
复制代码
// 将当前update对象放入 UpdaQueue的最后
function appendUpdateToQueue<State>( queue: UpdateQueue<State>, update: Update<State>, ) {
// Append the update to the end of the list.
if (queue.lastUpdate === null) {
// Queue is empty 队列为空 直接把lastUpdate 赋值为firstUpdate
queue.firstUpdate = queue.lastUpdate = update;
} else {
// 在queue中添加新的update
// 因此把当前update的lastUpdate的next 指向 新加的update
// 当前队列queue的lastUpdate 为新加的update
queue.lastUpdate.next = update;
queue.lastUpdate = update;
}
}
复制代码
点击进入 下图在线demopromise
异步执行的任务优先级都是较低的,为防止这个任务一直被打断,一直不能执行,因此react设置了一个expiraton-time。在某一个expiraton-time时间以前该任务能够被打断,当时间过时这个任务就会被强制执行。缓存
给节点的fiber 建立更新。reactDom.render是对于root节点的建立更新,setState和forceUpdate针对的是classComponent的状态进行更新渲染。性能优化