demo
更新DOM
export default function App() {
const [c, setC] = useState(0)
return (
<div onClick={() =>{
setC(c + 1)
}}>
{c}
</div>
);
}
commitMutationEffects
commitMutationEffects阶段主要进行DOM更新操作,下面看一下过程
if (flags & Ref) {
var current = nextEffect.alternate;
if (current !== null) {
commitDetachRef(current);
}
}
这一部分主要进行解绑ref的操作,因为dom可能发生变化,在改变dom前通过commitDetachRef进行解绑。
var primaryFlags = flags & (Placement | Update | Deletion | Hydrating);
这一段获取flag,demo没有涉及插入删除操作,因此获取到的flag是Update,然后进行更新操作
commitWork
case Update:
{
var _current3 = nextEffect.alternate;
commitWork(_current3, nextEffect);
break;
}
从switch case进入更新函数,调用commitWork,在commitWork内根据tag进入下面的逻辑
case HostComponent:
{
var instance = finishedWork.stateNode;
if (instance != null) {
var newProps = finishedWork.memoizedProps;
var oldProps = current !== null ? current.memoizedProps : newProps;
var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components.
var updatePayload = finishedWork.updateQueue;
finishedWork.updateQueue = null;
if (updatePayload !== null) {
commitUpdate(instance, updatePayload, type, oldProps, newProps);
}
}
return;
}
finishedWork.stateNode就是在completeWork阶段生成的DOM对象,此时引用的未更新的DOM。获取updateQueue后调用commitUpdate
commitUpdate
function commitUpdate(domElement, updatePayload, type, oldProps, newProps, internalInstanceHandle) {
updateFiberProps(domElement, newProps);
updateProperties(domElement, updatePayload, type, oldProps, newProps);
}
updateFiberProps将newProps挂载到dom的一个特殊id上(这个属性应该是根据某种算法生成的)
updateProperties调用updateDOMProperties
for (var i = 0; i < updatePayload.length; i += 2) {
var propKey = updatePayload[i];
var propValue = updatePayload[i + 1];
if (propKey === STYLE) {
setValueForStyles(domElement, propValue);
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
setInnerHTML(domElement, propValue);
} else if (propKey === CHILDREN) {
setTextContent(domElement, propValue);
} else {
setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
}
}
updateDOMProperties内遍历updateQueue,根据属性更新DOM。
下面换个demo,看一下插入DOM的逻辑
export default function App() {
const [c, setC] = useState(0)
const a = <div key="a">a</div>;
const b = <div key="b">b</div>;
return (
<div onClick={() =>{
setC(c + 1)
}}>
{c % 2 === 1 ? a : b}
</div>
);
}
触发更新后进入下面的部分
case Deletion:
{
commitDeletion(root, nextEffect);
break;
}
effectList的第一个fiber是workInProgress.alternate(先被打上deletion的fiber,下一个就是placement的fiber),然后调用相应的unmount函数,根据parent的情况调用removeChild,移除DOM节点
function removeChild(parentInstance, child) {
parentInstance.removeChild(child);
}
(其实到这个地方才知道effectList为啥要包含current fiber树上即将被删除的fiber,因为react内是删除DOM和添加DOM的操作,而不是替换,所以current fiber树和workInProgress fiber树上有变化的fiber都要加入到effectList)
看一下第二个effect的插入逻辑
case Placement:
{
commitPlacement(nextEffect);
nextEffect.flags &= ~Placement;
break;
}
在commitPlacement内调用下面的代码
if (isContainer) {
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
} else {
insertOrAppendPlacementNode(finishedWork, before, parent);
}
根据parent与node的关系调用不同的插入函数