赞
踩
1 )概述
commitRoot
第二个while循环中commitAllHostEffects
2 )源码
回到 commit 阶段的第二个循环中,在 commitRoot 函数里
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L690
看这里
nextEffect = firstEffect; startCommitHostEffectsTimer(); // 第二个循环调用的方法是 commitAllHostEffects // 这个就是主要是操作对于 dom 节点它需要去做的一些内容 // 比如说如果这个 dom 节点是刚刚新增的,那么要执行一个插入的操作 // 如果这个 dom 节点它需要被删除,我们要执行删除的操作 // 如果这个 dom 节点它的属性或者它 children 有更新,那么要执行一个更新的操作 while (nextEffect !== null) { let didError = false; let error; if (__DEV__) { invokeGuardedCallback(null, commitAllHostEffects, null); if (hasCaughtError()) { didError = true; error = clearCaughtError(); } } else { try { commitAllHostEffects(); } catch (e) { didError = true; error = e; } } if (didError) { invariant( nextEffect !== null, 'Should have next effect. This error is likely caused by a bug ' + 'in React. Please file an issue.', ); captureCommitPhaseError(nextEffect, error); // Clean-up if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } } } stopCommitHostEffectsTimer(); resetAfterCommit(root.containerInfo);
注意,在执行新的循环之前,都要执行 nextEffect = firstEffect;
因为每一个 effect 对象上面可能有不同的各种 effectTag
所以它们都是要再次经历一个循环之后去判断是否要进行对应的操作的
所以每一个循环开始之前,都要重新赋值成单项链表的开头
上面是使用的地方,现在定位到 commitAllHostEffects
// https://github.com/facebook/react/blob/v16.6.3/packages/react-reconciler/src/ReactFiberScheduler.js#L392 function commitAllHostEffects() { while (nextEffect !== null) { if (__DEV__) { ReactCurrentFiber.setCurrentFiber(nextEffect); } recordEffect(); const effectTag = nextEffect.effectTag; if (effectTag & ContentReset) { commitResetTextContent(nextEffect); } // 这个先跳过 if (effectTag & Ref) { const current = nextEffect.alternate; if (current !== null) { commitDetachRef(current); } } // The following switch statement is only concerned about placement, // updates, and deletions. To avoid needing to add a case for every // possible bitmap value, we remove the secondary effects from the // effect tag and switch on that value. let primaryEffectTag = effectTag & (Placement | Update | Deletion); switch (primaryEffectTag) { case Placement: { commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is inserted, before // any life-cycles like componentDidMount gets called. // TODO: findDOMNode doesn't rely on this any more but isMounted // does and isMounted is deprecated anyway so we should be able // to kill this. nextEffect.effectTag &= ~Placement; break; } // 先 Placement 后 Update case PlacementAndUpdate: { // Placement commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is inserted, before // any life-cycles like componentDidMount gets called. nextEffect.effectTag &= ~Placement; // 在 处理完 Placement 之后,就去掉 Placement // Update const current = nextEffect.alternate; commitWork(current, nextEffect); break; } case Update: { const current = nextEffect.alternate; commitWork(current, nextEffect); break; } case Deletion: { commitDeletion(nextEffect); break; } } nextEffect = nextEffect.nextEffect; } if (__DEV__) { ReactCurrentFiber.resetCurrentFiber(); } }
首先判断了一个叫 effectTag 是否有 ContentReset
commitResetTextContent
, 这个方法来自于 ReactFiberCommitWork.jsfunction commitResetTextContent(current: Fiber) {
if (!supportsMutation) {
return;
}
resetTextContent(current.stateNode);
}
// packages/react-dom/src/client/ReactDOMHostConfig.js#L339
export function resetTextContent(domElement: Instance): void {
setTextContent(domElement, '');
}
setTextContent
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import {TEXT_NODE} from '../shared/HTMLNodeType'; /** * Set the textContent property of a node. For text updates, it's faster * to set the `nodeValue` of the Text node directly instead of using * `.textContent` which will remove the existing node and create a new one. * * @param {DOMElement} node * @param {string} text * @internal */ let setTextContent = function(node: Element, text: string): void { if (text) { let firstChild = node.firstChild; // 这里,一个first === last 代表只有一个节点,才会设置,防止有其他节点导致误删除了其他节点 if ( firstChild && firstChild === node.lastChild && firstChild.nodeType === TEXT_NODE ) { firstChild.nodeValue = text; return; } } node.textContent = text; }; export default setTextContent;
接下去,primaryEffectTag
接下去就是赋值 nextEffect,然后再次进入这个循环
直到我们整个单链表循环完成之后,它就结束了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。