源码,过一下diff算法的细节!!!!!
入口位置 react/packages/react-reconciler/src/ReactFiberWorkLoop.new.js
这里源码做了删减,只保存了主流程
function workLoopConcurrent() { while (workInProgress !== null && !shouldYield()) { performUnitOfWork(workInProgress); } }function performUnitOfWork(unitOfWork: Fiber): void { // alternate指向的是另一颗fiber树映射的节点,注意下这一点,在后面处理函数组件中有用到 const current = unitOfWork.alternate; setCurrentDebugFiberInDEV(unitOfWork); let next; next = beginWork(current, unitOfWork, renderLanes); if (next === null) { completeUnitOfWork(unitOfWork); } else { workInProgress = next; } ReactCurrentOwner.current = null; }
beginWork 即向下递的过程,生成fiber节点,diff,复用,打上自身节点的tag
这里就对不同的组件做了不同的处理,例如根节点,函数组件,类组件,memo,fowordRef,文本节点等,但是逻辑基本都是差不多的,就只看下函数组件咋处理的
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
witch (workInProgress.tag) {
case FunctionComponent: {
// type其实就是我们的那个函数,并不是createElement
const Component = workInProgress.type;
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
// 其他 case 语句...
}}
updateFunctionComponent
处理函数组件 renderWithHooks执行了我们的组件函数,reconcileChildren,处理的children,也就是diif阶段(这里只会处理sibling,并不会处理sibling的子节点),然后返回当前节点的firstChild
function updateFunctionComponent(
current,
workInProgress,
Component,
nextProps: any,
renderLanes,
) {
nextChildren = renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderLanes,
);
workInProgress.flags |= PerformedWork;
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
renderWithHooks重置fiber的memoizedState与updateQueue即保存hooks数据的链表和副作用队列
然后根据current,来判断是否是挂载 还是更新,current是当前页面展示的这一棵fiber树workInProgress是我们在缓存中处理的这颗fiber树。也就是判断当前fiber对应的另一个fiber是否存在或者缓存的另一个fiber节点的hooks链表是否为空(这里需要了解下fiber的数据结构啊,const current = unitOfWork.alternate;alternate指向了另一颗fiber映射的节点)
然后执行了我们的组件函数,在重置ReactCurrentDispatcher.current为默认,最后返回children
// 进入
function renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderLanes,
){
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes;
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
}
let children = Component(props, secondArg);
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
return children;
}
向下递的流程
reconcileChildren看下diff算法,这里只关注下childrn为Array的情况
function reconcileChildFibers(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
lanes: Lanes,
): Fiber | null {
const isUnkeyedTopLevelFragment =
typeof newChild === 'object' &&
newChild !== null &&
newChild.type === REACT_FRAGMENT_TYPE &&
newChild.key === null;
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children;
}
if (isArray(newChild)) {
return reconcileChildrenArray(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
}
if (getIteratorFn(newChild)) {
return reconcileChildrenIterator(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
}
throwOnInvalidObjectType(returnFiber, newChild);
}
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
diff算法
两遍for循环
在这个过程中,给当前fiber节点打上 tag(增,删,改)
updateSlot,对比 vdom 和老的 fiber根据节点的key和type对比,如果相同则复用老的fiber节点(会更改一些属性),在遍历过程中发现不同则结束第一次遍历,并记录下结束时fiber指针和children的index。如果所有的新的 vdom 处理完了,那就把剩下的老 fiber 节点删掉就行。
如果还有 vdom 没处理,就进行第二次遍历,把剩下的老 fiber 放到 map 里,遍历剩下的 vdom,从 map 里查找,如果找到了,就移动过来,打上修改的标记(因为这时他们的顺序已经变了)。
第二轮遍历完了之后,把剩余的老 fiber 删掉,剩余的 vdom 新增。
function reconcileChildrenArray(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChildren: Array<*>,
lanes: Lanes,
): Fiber | null {
let resultingFirstChild: Fiber | null = null;
let previousNewFiber: Fiber | null = null;
let oldFiber = currentFirstChild;
let lastPlacedIndex = 0;
let newIdx = 0;
let nextOldFiber = null;
// 第一轮遍历,复用fiber节点
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
if (oldFiber.index > newIdx) {
nextOldFiber = oldFiber;
oldFiber = null;
} else {
nextOldFiber = oldFiber.sibling;
}
const newFiber = updateSlot(
returnFiber,
oldFiber,
newChildren[newIdx],
lanes,
);
if (newFiber === null) {
if (oldFiber === null) {
oldFiber = nextOldFiber;
}
break;
}
if (shouldTrackSideEffects) {
if (oldFiber && newFiber.alternate === null) {
deleteChild(returnFiber, oldFiber);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
oldFiber = nextOldFiber;
}
if (newIdx === newChildren.length) {
deleteRemainingChildren(returnFiber, oldFiber);
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
if (oldFiber === null) {
// since the rest will all be insertions.
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
if (newFiber === null) {
continue;
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
// TODO: Move out of the loop. This only happens for the first run.
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
// 余下fiber节点转map(oldFiber时第一轮遍历结束时,指向的fiber节点)
const existingChildren = mapRemainingChildren(returnFiber, oldFiber);
// 第二轮遍历,从map中根据我们设置的key去找
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = updateFromMap(
existingChildren,
returnFiber,
newIdx,
newChildren[newIdx],
lanes,
);
if (newFiber !== null) {
if (shouldTrackSideEffects) {
if (newFiber.alternate !== null) {
existingChildren.delete(
newFiber.key === null ? newIdx : newFiber.key,
);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
}
if (shouldTrackSideEffects) {
existingChildren.forEach(child => deleteChild(returnFiber, child));
}
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
递到低,向上冒泡
调用completeWork处理当前节点
处理兄弟节点,如果兄弟节点存在则更改workInProgress指针指向(即兄弟节点开始beginWork流程)
function completeUnitOfWork(unitOfWork: Fiber): void {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
// 主流程
let next = completeWork(current, completedWork, renderLanes);
if (next !== null) {
workInProgress = next;
return;
}
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
workInProgress = siblingFiber;
return;
}
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
if (workInProgressRootExitStatus === RootInProgress) {
workInProgressRootExitStatus = RootCompleted;
}
}
completeWork
主要看下HostComponent类型的处理,也就是我们的html元素类型
stateNode即fiber节点映射的真实dom,如果存在的话做则根据props对原先的dom做更新操作,
不存在的话则需要创建,然后所有子节点添加到新创建的实例中
向上冒泡副作用标记,确保父节点能够知晓子节点的变化(也就是计算父节点的subTreeTag)
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case HostComponent: {
popHostContext(workInProgress);
const type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
updateHostComponent(current, workInProgress, type, newProps);
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
} else {
const currentHostContext = getHostContext();
const wasHydrated = popHydrationState(workInProgress);
if (wasHydrated) {
if (
prepareToHydrateHostInstance(workInProgress, currentHostContext)
) {
markUpdate(workInProgress);
}
} else {
const rootContainerInstance = getRootHostContainer();
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
appendAllChildren(instance, workInProgress, false, false);
workInProgress.stateNode = instance;
if (
finalizeInitialChildren(
instance,
type,
newProps,
currentHostContext,
)
) {
markUpdate(workInProgress);
}
}
if (workInProgress.ref !== null) {
markRef(workInProgress);
}
}
bubbleProperties(workInProgress);
return null;
}
}
OK!
共有 0 条评论