Skip to content

Commit d9b41c9

Browse files
committed
feat(reconciler): 新增 更新阶段 源码注释
1 parent b209033 commit d9b41c9

11 files changed

+109
-14
lines changed

README-ME.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,4 +536,29 @@ react 为不同的更新划分了不同的层级
536536
- [函数式组件]() 先调用 [safelyCallCommitHookLayoutEffectListMount](https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberCommitWork.old.js#L2416) 然后调用 [commitHookEffectListMount](https://github.com/facebook/react/blob/e0d9b289998c66d2e4fb75582b9e107054e4e0a4/packages/react-reconciler/src/ReactFiberCommitWork.old.js#L568) 在 `commitHookEffectListMount` 中 使用 `do while` 循环 **执行了 所有effect链表中的 副作用函数** 并且 **记录了 所有effect链表中的 销毁函数**
537537
- [类组件]() 先调用 [safelyCallComponentDidMount](https://github.com/facebook/react/blob/e0d9b289998c66d2e4fb75582b9e107054e4e0a4/packages/react-reconciler/src/ReactFiberCommitWork.old.js#L2423) 然后调用 类组件的 [`componentDidMount`](https://github.com/facebook/react/blob/e0d9b289998c66d2e4fb75582b9e107054e4e0a4/packages/react-reconciler/src/ReactFiberCommitWork.old.js#L253) 生命周期函数
538538
539-
## diff 算法
539+
## diff 算法
540+
541+
## 数组情况
542+
如果 key 不一致,则直接跳出对比,接着取到所有的 oldChild, 存到 map中 key 等于 key,value 等于 fiber 对象,[这里可以体现](https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactChildFiber.old.js#L842),
543+
然后遍历新 child,从 旧的 child 中的 map 中尝试 [取新child中对应的key](https://github.com/facebook/react/blob/23c80959ad79898877e76a61dffbe8c02ec3853e/packages/react-reconciler/src/ReactChildFiber.old.js#L631),然后再从 map 中 [将其删除](https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactChildFiber.old.js#L860)
544+
545+
## 单节点情况
546+
### 校验新旧 props 是否一致
547+
``` js
548+
updateHostComponent = function(
549+
current: Fiber,
550+
workInProgress: Fiber,
551+
type: Type,
552+
newProps: Props,
553+
rootContainerInstance: Container,
554+
) {
555+
const oldProps = current.memoizedProps;
556+
if (oldProps === newProps) {
557+
// //在 mutation 模式下,这足以进行救助,因为即使孩子改变了,我们也不会触及这个节点。
558+
// In mutation mode, this is sufficient for a bailout because
559+
// we won't touch this node even if children changed.
560+
return;
561+
}
562+
// ...若干行代码
563+
}
564+
```

src/App.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,36 @@ function App() {
2727
}
2828
}, [])
2929

30-
const [conut, setCount] = useState(1)
30+
const [count, setCount] = useState(1)
3131

3232
// 事件系统
3333
// return <EventDemo/>
3434

3535
// return <Hooks/>
3636
// fiber树
37+
38+
const a = <div>
39+
<h2 key="hello">hello</h2>
40+
<p key="world">world</p>
41+
</div>
42+
43+
// 顺序不一致
44+
// const b = <div>
45+
// <p key="world">world</p>
46+
// <h2 key="hello">hello</h2>
47+
// </div>
48+
49+
// 类型不一致
50+
const b = <div>
51+
<h6 key="hello">hello</h6>
52+
<p key="world">world</p>
53+
</div>
54+
3755
return (
38-
<div className="App">
39-
<span onClick={() => {
40-
debugger
41-
setCount(conut + 1);
42-
}}>我是span {conut}</span>
43-
<h2>我是h2</h2>
56+
<div className="App" onClick={() => setCount(count + 1)}>
57+
{count % 2 ? a : b}
58+
{/* <span>我是span {count}</span> */}
59+
{/* <h2>我是h2</h2> */}
4460
</div>
4561
);
4662
{/* <span className={'app-span'} onClick={() => setCount(count + 1)}>App{count}</span> */ }

src/react/v17.0.2/react-dom/src/client/ReactDOMComponent.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -608,18 +608,20 @@ export function setInitialProperties(
608608
}
609609
}
610610

611+
// 计算两个对象之间的差异。
611612
// Calculate the diff between the two objects.
612613
export function diffProperties(
613-
domElement: Element,
614-
tag: string,
615-
lastRawProps: Object,
616-
nextRawProps: Object,
617-
rootContainerElement: Element | Document,
614+
domElement: Element, // 旧的 真实 dom
615+
tag: string, // 元素标签 div | h2 等
616+
lastRawProps: Object, // 旧的 props
617+
nextRawProps: Object, // 新的 props
618+
rootContainerElement: Element | Document, // 根容器
618619
): null | Array<mixed> {
619620
if (__DEV__) {
620621
validatePropertiesInDevelopment(tag, nextRawProps);
621622
}
622623

624+
// 初始化 updatePayload
623625
let updatePayload: null | Array<any> = null;
624626

625627
let lastProps: Object;
@@ -663,7 +665,10 @@ export function diffProperties(
663665
let propKey;
664666
let styleName;
665667
let styleUpdates = null;
668+
669+
// 遍历旧的 props
666670
for (propKey in lastProps) {
671+
// 如果 新的 props 中包含旧的 的props 中的这个 属性key,则跳过
667672
if (
668673
nextProps.hasOwnProperty(propKey) ||
669674
!lastProps.hasOwnProperty(propKey) ||
@@ -703,16 +708,22 @@ export function diffProperties(
703708
(updatePayload = updatePayload || []).push(propKey, null);
704709
}
705710
}
711+
712+
// 遍历新的 props
706713
for (propKey in nextProps) {
714+
// 取到 新的props 和旧的这次 遍历到的 值,旧的 props 有可能是 undefined
707715
const nextProp = nextProps[propKey];
708716
const lastProp = lastProps != null ? lastProps[propKey] : undefined;
717+
718+
// 如果 新旧的属性值相等,则跳过,
709719
if (
710720
!nextProps.hasOwnProperty(propKey) ||
711721
nextProp === lastProp ||
712722
(nextProp == null && lastProp == null)
713723
) {
714724
continue;
715725
}
726+
716727
if (propKey === STYLE) {
717728
if (__DEV__) {
718729
if (nextProp) {
@@ -767,7 +778,9 @@ export function diffProperties(
767778
// TODO: It might be too late to clear this if we have children
768779
// inserted already.
769780
}
770-
} else if (propKey === CHILDREN) {
781+
} else if (propKey === CHILDREN) { // children 发生变化
782+
// 如果 children 是 字符串或者 数字类型,则直接push,
783+
// 也是因为这里的原因,所以最后 commit 的时候 第 i 项是更新的key, 第 i+1 项是 更新的值
771784
if (typeof nextProp === 'string' || typeof nextProp === 'number') {
772785
(updatePayload = updatePayload || []).push(propKey, '' + nextProp);
773786
}
@@ -787,6 +800,9 @@ export function diffProperties(
787800
}
788801
}
789802
if (!updatePayload && lastProp !== nextProp) {
803+
// onClick 会进入到这里
804+
// 这是一个特例。如果任何侦听器更新,我们需要确保“current”props指针得到更新,
805+
// 因此我们需要一个commit来更新这个元素。
790806
// This is a special case. If any listener updates we need to ensure
791807
// that the "current" props pointer gets updated so we need a commit
792808
// to update this element.
@@ -802,6 +818,8 @@ export function diffProperties(
802818
// ID so client and server IDs match and throws to rerender.
803819
nextProp.toString();
804820
} else {
821+
//对于任何其他属性,我们总是将它添加到队列中,然后我们
822+
//在提交期间使用允许的属性列表过滤掉它。
805823
// For any other property we always add it to the queue and then we
806824
// filter it out using the allowed property list during the commit.
807825
(updatePayload = updatePayload || []).push(propKey, nextProp);

src/react/v17.0.2/react-dom/src/client/ReactDOMHostConfig.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ export function finalizeInitialChildren(
324324
return shouldAutoFocusHostComponent(type, props);
325325
}
326326

327+
// 准备更新
327328
export function prepareUpdate(
328329
domElement: Instance,
329330
type: string,
@@ -347,6 +348,7 @@ export function prepareUpdate(
347348
validateDOMNesting(null, string, ownAncestorInfo);
348349
}
349350
}
351+
// diff 元素属性,如果没有任何变化则返回的是 null
350352
return diffProperties(
351353
domElement,
352354
type,

src/react/v17.0.2/react-reconciler/src/ReactChildFiber.old.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ function ChildReconciler(shouldTrackSideEffects) {
570570
if (typeof newChild === 'object' && newChild !== null) {
571571
switch (newChild.$$typeof) {
572572
case REACT_ELEMENT_TYPE: {
573+
// 如果两个key 一致,则进行更新
573574
if (newChild.key === key) {
574575
return updateElement(returnFiber, oldFiber, newChild, lanes);
575576
} else {
@@ -733,6 +734,12 @@ function ChildReconciler(shouldTrackSideEffects) {
733734
newChildren: Array<*>,
734735
lanes: Lanes,
735736
): Fiber | null {
737+
738+
// 这个算法不能通过从两端搜索来优化,因为我们在纤维上没有反向指针。我想看看这个模型能走多远。如果它最终不值得进行权衡,我们可以稍后添加它。
739+
// 即使是两个端点的优化,我们也希望优化的情况下,有很少的改变和暴力的比较,而不是去 Map。它会首先在forward-only模式中探索命中那条路径,并且只有当我们注意到我们需要很多前瞻性时才会去寻找 Map。这不能像两个结束的搜索那样处理反转,但这是不寻常的。此外,为了使两端优化在Iterables上工作,我们需要复制整个集合。
740+
// 在第一次迭代中,我们只会在每次插入/移动时碰到坏情况(向Map添加所有内容)。
741+
// 如果你改变这个代码,也更新reconcileChildrenIterator(),它使用相同的算法。
742+
736743
// This algorithm can't optimize by searching from both ends since we
737744
// don't have backpointers on fibers. I'm trying to see how far we can get
738745
// with that model. If it ends up not being worth the tradeoffs, we can
@@ -768,6 +775,7 @@ function ChildReconciler(shouldTrackSideEffects) {
768775
let lastPlacedIndex = 0;
769776
let newIdx = 0;
770777
let nextOldFiber = null;
778+
771779
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
772780
if (oldFiber.index > newIdx) {
773781
nextOldFiber = oldFiber;
@@ -858,6 +866,8 @@ function ChildReconciler(shouldTrackSideEffects) {
858866
// current, that means that we reused the fiber. We need to delete
859867
// it from the child list so that we don't add it to the deletion
860868
// list.
869+
// 新 fiber 还在工作中,但如果有电流,就意味着我们可以重复使用纤维。
870+
// 我们需要将其从子列表中删除,这样就不会将其添加到删除列表中。
861871
existingChildren.delete(
862872
newFiber.key === null ? newIdx : newFiber.key,
863873
);
@@ -1133,6 +1143,7 @@ function ChildReconciler(shouldTrackSideEffects) {
11331143
elementType.$$typeof === REACT_LAZY_TYPE &&
11341144
resolveLazy(elementType) === child.type)
11351145
) {
1146+
// 删除其它兄弟节点,因为当前为调和单个子节点,表示只有一个子节点
11361147
deleteRemainingChildren(returnFiber, child.sibling);
11371148
const existing = useFiber(child, element.props);
11381149
existing.ref = coerceRef(returnFiber, child, element);

src/react/v17.0.2/react-reconciler/src/ReactFiber.old.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
257257
let workInProgress = current.alternate;
258258

259259
// 如果当前只有一个 fiber ,只有在第一次render 的时候才是 null
260+
//区分是在mount时还是在update时
260261
if (workInProgress === null) {
261262
// We use a double buffering pooling technique because we know that we'll
262263
// only ever need at most two versions of a tree. We pool the "other" unused
@@ -292,6 +293,7 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
292293
workInProgress.alternate = current;
293294
current.alternate = workInProgress;
294295
} else {
296+
// update 时 复用 属性
295297
workInProgress.pendingProps = pendingProps;
296298
// Needed because Blocks store data on type.
297299
workInProgress.type = current.type;

src/react/v17.0.2/react-reconciler/src/ReactFiberBeginWork.old.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ export function reconcileChildren(
256256
nextChildren: any,
257257
renderLanes: Lanes,
258258
) {
259+
// debugger
259260
if (current === null) {
260261
// If this is a fresh new component that hasn't been rendered yet, we
261262
// won't update its child set by applying minimal side-effects. Instead,

src/react/v17.0.2/react-reconciler/src/ReactFiberCommitWork.old.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,6 +2283,7 @@ function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
22832283
commitWork(current, finishedWork);
22842284
break;
22852285
}
2286+
// 更新
22862287
case Update: {
22872288
const current = finishedWork.alternate;
22882289
commitWork(current, finishedWork);

src/react/v17.0.2/react-reconciler/src/ReactFiberCompleteWork.old.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ import {
150150
} from './ReactFiberCacheComponent.old';
151151

152152
function markUpdate(workInProgress: Fiber) {
153+
// 标记具有更新效果的 fiber。这将Placement变成PlacementAndUpdate。
153154
// Tag the fiber with an update effect. This turns a Placement into
154155
// a PlacementAndUpdate.
155156
workInProgress.flags |= Update;
@@ -241,22 +242,28 @@ if (supportsMutation) {
241242
updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {
242243
// Noop
243244
};
245+
246+
// 更新原生 html 元素
244247
updateHostComponent = function(
245248
current: Fiber,
246249
workInProgress: Fiber,
247250
type: Type,
248251
newProps: Props,
249252
rootContainerInstance: Container,
250253
) {
254+
// 如果我们有一个 alternate ,这意味着这是一个更新,我们需要安排一个副作用来进行更新。
251255
// If we have an alternate, that means this is an update and we need to
252256
// schedule a side-effect to do the updates.
253257
const oldProps = current.memoizedProps;
254258
if (oldProps === newProps) {
259+
// //在 mutation 模式下,这足以进行救助,因为即使孩子改变了,我们也不会触及这个节点。
255260
// In mutation mode, this is sufficient for a bailout because
256261
// we won't touch this node even if children changed.
257262
return;
258263
}
259264

265+
// 如果我们因为我们的一个孩子更新而更新,我们没有 newProps 所以我们必须重用它们。
266+
// TODO: 将更新 API 拆分为 props 与 children 的单独部分。 如果孩子们根本不是特殊情况,那就更好了。
260267
// If we get updated because one of our children updated, we don't
261268
// have newProps so we'll have to reuse them.
262269
// TODO: Split the update API as separate for the props vs. children.
@@ -266,6 +273,8 @@ if (supportsMutation) {
266273
// TODO: Experiencing an error where oldProps is null. Suggests a host
267274
// component is hitting the resume path. Figure out why. Possibly
268275
// related to `hidden`.
276+
277+
// 对比新旧 props 的变化,如果没有任何变化会返回 null
269278
const updatePayload = prepareUpdate(
270279
instance,
271280
type,
@@ -276,9 +285,12 @@ if (supportsMutation) {
276285
);
277286
// TODO: Type this specific to this type of component.
278287
workInProgress.updateQueue = (updatePayload: any);
288+
//如果更新有效负载表明有更改或
289+
//是一个新的 ref,我们将其标记为更新。所有的工作都在 commitWork 中完成。
279290
// If the update payload indicates that there is a change or if there
280291
// is a new ref we mark this as an update. All the work is done in commitWork.
281292
if (updatePayload) {
293+
//标记具有更新效果的光纤。这将Placement变成PlacementAndUpdate。
282294
markUpdate(workInProgress);
283295
}
284296
};
@@ -867,6 +879,7 @@ function completeWork(
867879

868880
// 更新 阶段
869881
if (current !== null && workInProgress.stateNode != null) {
882+
// 更新原生 html 元素
870883
updateHostComponent(
871884
current,
872885
workInProgress,

src/react/v17.0.2/react-reconciler/src/ReactFiberHooks.old.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,12 +1932,16 @@ function dispatchAction<S, A>(
19321932
fiber === currentlyRenderingFiber ||
19331933
(alternate !== null && alternate === currentlyRenderingFiber)
19341934
) {
1935+
// 这是一个渲染阶段更新。把它藏在一个懒惰创建的地图中
1936+
// 队列 -> 更新链表。在此渲染过程之后,我们将重新启动
1937+
// 并将隐藏的更新应用到 work-in-progress hook 之上。
19351938
// This is a render phase update. Stash it in a lazily-created map of
19361939
// queue -> linked list of updates. After this render pass, we'll restart
19371940
// and apply the stashed updates on top of the work-in-progress hook.
19381941
didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
19391942
const pending = queue.pending;
19401943
if (pending === null) {
1944+
// 这是第一次更新。创建一个循环列表。
19411945
// This is the first update. Create a circular list.
19421946
update.next = update;
19431947
} else {
@@ -2017,6 +2021,7 @@ function dispatchAction<S, A>(
20172021
warnIfNotCurrentlyActingUpdatesInDev(fiber);
20182022
}
20192023
}
2024+
// 开始调度更新
20202025
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
20212026

20222027
if (isTransitionLane(lane) && root !== null) {

0 commit comments

Comments
 (0)