# 源码(4)
# beginWork
React 根据 workInProgress.tag 会去走不同的分支,都有那些 tag 呢? 在 Tag (opens new window) (opens new window)我们可以看到所有的 Tag 类型
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const FundamentalComponent = 20;
export const ScopeComponent = 21;
export const Block = 22;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
因为从根节点开始遍历,所以第一次肯定是 HostRoot, 因此我们进入到 updateHostRoot 中
# HostRoot
...
const nextState = workInProgress.memoizedState;
// Caution: React DevTools currently depends on this property
// being called "element".
const nextChildren = nextState.element;
...
reconcileChildren(
current,
worInProgress,
nextChildren,
renderExpirationTime,
);
...
return workInProgress.child;
2
3
4
5
6
7
8
9
10
11
12
13
14
里面会判断是不是初次,如果是初次会创建 Fiber 节点,非初次 更新 Fiber 节点,打上EffectTag标记 最后返回 workInProgress.child
# reconcileChildren
export function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderExpirationTime: ExpirationTime,
) {
if (current === null) {
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderExpirationTime,
);
} else {
// 1. 得到新的Fiber节点,dom-diff, 打上EffectTag标记
workInProgress.child = reconcileChildFibers(
workInProgress,
current.child,
nextChildren,
renderExpirationTime,
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
reconcileChildren 分别调用 mountChildFibers 和 reconcileChildFibers 其实他们都是一个方法 知识区分了是否初次渲染 如下:
export const reconcileChildFibers = ChildReconciler(true);
export const mountChildFibers = ChildReconciler(false);
2
# ChildReconciler
ChildReconciler (opens new window) (opens new window)里面的方法非常之多,下面例举几个比较重要的方法来看看
function deleteChild(returnFiber,childToDelete){
...
}
...
function reconcileChildrenArray()
function reconcileSingleTextNode()
function reconcileSingleElement()
function reconcileChildFibers(returnFiber,currentFirstChild,newChild,expirationTime){
const isUnkeyedTopLevelFragment =
typeof newChild === 'object' &&
newChild !== null &&
newChild.type === REACT_FRAGMENT_TYPE &&
newChild.key === null;
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children;
}
const isObject = typeof newChild === 'object' && newChild !== null;
if (isObject) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(
reconcileSingleElement(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
),
);
case REACT_PORTAL_TYPE:
return placeSingleChild(
reconcileSinglePortal(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
),
);
}
}
if (typeof newChild === 'string' || typeof newChild === 'number') {
return placeSingleChild(
reconcileSingleTextNode(
returnFiber,
currentFirstChild,
'' + newChild,
expirationTime,
),
);
}
if (isArray(newChild)) {
return reconcileChildrenArray(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
);
}
if (getIteratorFn(newChild)) {
return reconcileChildrenIterator(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
);
}
if (isObject) {
throwOnInvalidObjectType(returnFiber, newChild);
}
...
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
return reconcileChildFibers;
function placeSingleChild(newFiber: Fiber): Fiber {
// This is simpler for the single child case. We only need to do a
// placement for inserting new children.
if (shouldTrackSideEffects && newFiber.alternate === null) {
newFiber.effectTag = Placement;
}
return newFiber;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
reconcileChildFibers 根据 JSX 生成的 $$typeof (opens new window) (opens new window)去打上不同的操作标记,如初次就是新增 Placement 在 ReactSideEffectTags (opens new window) (opens new window)我们可以看到所有的 副作用
export const NoEffect = /* */ 0b0000000000000;
export const PerformedWork = /* */ 0b0000000000001;
// You can change the rest (and add more).
export const Placement = /* */ 0b0000000000010;
export const Update = /* */ 0b0000000000100;
export const PlacementAndUpdate = /* */ 0b0000000000110;
export const Deletion = /* */ 0b0000000001000;
export const ContentReset = /* */ 0b0000000010000;
export const Callback = /* */ 0b0000000100000;
export const DidCapture = /* */ 0b0000001000000;
export const Ref = /* */ 0b0000010000000;
export const Snapshot = /* */ 0b0000100000000;
export const Passive = /* */ 0b0001000000000;
export const Hydrating = /* */ 0b0010000000000;
export const HydratingAndUpdate = /* */ 0b0010000000100;
// Passive & Update & Callback & Ref & Snapshot
export const LifecycleEffectMask = /* */ 0b0001110100100;
// Union of all host effects
export const HostEffectMask = /* */ 0b0011111111111;
export const Incomplete = /* */ 0b0100000000000;
export const ShouldCapture = /* */ 0b1000000000000;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# reconcileSingleElement
function reconcileSingleElement(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
element: ReactElement,
expirationTime: ExpirationTime,
): Fiber {
const key = element.key;
let child = currentFirstChild;
// 深度优先遍历
while (child !== null) {
// TODO: If key === null and child.key === null, then this only applies to
// the first item in the list.
// 比较key是否一样
if (child.key === key) {
switch (child.tag) {
case Fragment: {
if (element.type === REACT_FRAGMENT_TYPE) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props.children);
existing.return = returnFiber;
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
break;
}
case Block:
if (enableBlocksAPI) {
if (
element.type.$$typeof === REACT_BLOCK_TYPE &&
element.type.render === child.type.render
) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props);
existing.type = element.type;
existing.return = returnFiber;
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
}
// We intentionally fallthrough here if enableBlocksAPI is not on.
// eslint-disable-next-lined no-fallthrough
default: {
if (
child.elementType === element.type ||
// Keep this check inline so it only runs on the false path:
(__DEV__
? isCompatibleFamilyForHotReloading(child, element)
: false)
) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props);
existing.ref = coerceRef(returnFiber, child, element);
existing.return = returnFiber;
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
break;
}
}
// Didn't match.
deleteRemainingChildren(returnFiber, child);
break;
} else {
// 不一样则删除
deleteChild(returnFiber, child);
}
child = child.sibling;
}
if (element.type === REACT_FRAGMENT_TYPE) {
const created = createFiberFromFragment(
element.props.children,
returnFiber.mode,
expirationTime,
element.key,
);
created.return = returnFiber;
return created;
} else {
// 初次创建Fiber,和父节点连接在一起
const created = createFiberFromElement(
element,
returnFiber.mode,
expirationTime,
);
created.ref = coerceRef(returnFiber, currentFirstChild, element);
created.return = returnFiber;
return created;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
- while是找到新老children中第一个key和节点类型相同的节点,直接复用节点。然后删除剩余老的children。
- 根据类型不同创建不同的Fiber。
- 把创建的 Fiber 的 return 指向 returnFiber,构建父子关系。
# reconcileChildrenArray
reconcileChildrenArray (opens new window) (opens new window)diff 算法就在这里,代码太多了
# 总结
在 beginWork 会根据 Tag 的不同进入不同的分支
- 如果 type 是 function 且是类组件则 fiberTag 为 ClassComponent
- 如果 type 是 string 则fiberTag 为 HostComponent
- 调用createFiber创建Fiber
- 给Fiber挂载 elementType、type和expirationTime属性。
最后这个Fiber在 reconcilerChildren 方法中赋值给 workInProgress.child 。
在 performUnitOfWork 方法中赋值给 next,最后返回。
在 workLoopSync 方法中赋值给 workInProgress 。从而继续while循环。
调和子节点是创建不同类型的Fiber过程的,最终会把创建的Fiber挂载到 workInProgress 的 child属性上。而且是由上到下,这件就构建了Fiber树,这章我们看完了 HostRoot,下一章我们去看下 ClassComponent 分支