本次实验的demo如下
export default function App() {
const [toggle, setToggle] = useState(true);
const a = <div key="a">a</div>;
const b = <div key="b">b</div>;
return (
<div
onClick={() => {
setToggle(!toggle);
}}
>
{toggle ? a : b}
</div>
);
}
performUnitOfWork
第一次节点切换时先看一下performUnitOfWork
var current = unitOfWork.alternate;
current指向的是workInProgress的同层fiber,react的diff是基于同层fiber的对比
performUnitOfWork内调用了beginWork,看一下beginWork的参数。
此时current.pendingProps.children === current.memoizedProps.children
{
$$typeof: Symbol(react.element)
key: "a"
props: {children: "a"}
ref: null
type: "div"
}
workInProgress的memoizedProps.children
{
$$typeof: Symbol(react.element)
key: "a"
props: {children: "a"}
ref: null
type: "div"
}
而pendingProps.children变成了
{
$$typeof: Symbol(react.element)
key: "b"
props: {children: "b"}
ref: null
type: "div"
}
也就是说jsx对象的变化先出现在了workInProgress的pendingProps上,下面对比oldProps和newProps
var oldProps = current.memoizedProps;
var newProps = workInProgress.pendingProps;
if (oldProps !== newProps){
didReceiveUpdate = true; //ture
}
根据workInProgress的tag调用updateHostComponent
updateHostComponent
var nextProps = workInProgress.pendingProps;
var prevProps = current !== null ? current.memoizedProps : null;
var nextChildren = nextProps.children;
// some code
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
updateHostComponet获取pedingProps和memoizedProps然后调用reconcileChildren
reconcileChildren
此时收到的三个参数
- current是workInProgress.alternate,详见performUnitOfWork
- workInProgress,当前的核心feiber
- nextChildren是workInProgress的pendingProps,新的jsx体现在该属性上
reconcileChildren内部调用reconcileChildFibers, 该函数和mountChildFibers都是childReconciler的返回值
workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);
reconcileChildFibers
前三个参数:
- workInProgress fiber
- workInProgress.alternate.child
- workInProgress.pendingProps.children
reconcileChildFibers内调用reconcileSingleElement(单节点diff)和placeSingleChild
if (isObject) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));
case REACT_PORTAL_TYPE:
// some code
}
}
reconcileSingleElement(单节点diff)
reconcileSIngleElement根据child != null进入update还是mount, 此时进行update
while (child !== null) {
if (child.key === key) {
switch (child.tag) {
case Fragment:
{
// some code
}
case Block:
default:
{
if (child.elementType === element.type || (isCompatibleFamilyForHotReloading(child, element) )) {
// some code
} else {
deleteChild(returnFiber, child);
}
child.key === ‘a’ 而 element.key === b,节点无法复用,直接删除,复用的逻辑以后会补充
deleteChild(returnFiber, child);
|
|
\|/
childToDelete.flags = Deletion;
先不考虑effect的逻辑,当fiber无法复用时给current.child打上Deletion flag
由于fiber无法复用,因此创建新的fiber
var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
_created4.ref = coerceRef(returnFiber, currentFirstChild, element);
_created4.return = returnFiber;
return _created4;
placeSingleChild
if (shouldTrackSideEffects && newFiber.alternate === null) {
newFiber.flags = Placement;
}
Placement是代表插入的flag,给新的fiber当上Placement
其实fiber对比值得就是current.child fiber与新的jsx(workInProgress.pendingProps.children)之间的对比,无法复用就给current.child打上Deletion flag,同时根据新的jsx创建新的fiber挂载到workInProgress.child上,同时打上Placement flag。
summary
总结一下reconcile相关函数的调用关系
single Diff的工作流程