1
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
|
// ReactDOM 的渲染模式
export type RootTag = 0 | 1 | 2;
export const LegacyRoot = 0; // ReactDOM.createLegacyRoot
export const BlockingRoot = 1; // ReactDOM.createBlockingRoot
export const ConcurrentRoot = 2; // ReactDOM.createRoot
// ReactDOM.render
export function render(
element: React$Element<any>,
container: Container,
callback: ?Function
) {
// 向 DOMContainer 里渲染子树
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback
);
}
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>, // null
children: ReactNodeList, // ReactElement
container: Container, // DOMContainer
forceHydrate: boolean, // false
callback: ?Function // () => {}
) {
let root: RootType = container._reactRootContainer;
let fiberRoot;
if (!root) {
/**
* 初始化阶段准备工作:
* 0. legacyCreateRootFromDOMContainer 最终会调用 createRootImpl
* 1. container._reactRootContainer._internalRoot = createRootImpl(container, 0, undefined)
* 2. createRootImpl(container, 0, undefined) 返回了 fiberRoot
* 3. fiberRoot 一个应用只有一个,其 current 属性指向了 rootFiberNode
* 4. FiberRoot.current 指向了 currentFiber 树
* 5. FiberRoot.finishedWork 指向了 workInProgress 树
* 6. 清空 container
*/
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate
);
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
/**
* getPublicRootInstance 会通过 fiberRoot 访问 rootFiberNode
* 如果 rootFiberNode 没有子节点,返回 null
* 否则返回 rootFiberNode 子节点的 stateNode
* 总之就是返回一个非 rootFiberNode 的组建实例
*/
const instance = getPublicRootInstance(fiberRoot);
// 调用了 callback 就说明应用初始化完成了
originalCallback.call(instance);
};
}
// 初始化完成之后,就准备进行应用渲染了
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
// 后续更新需要重新渲染,会走这里
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
function createRootImpl(
container: Container,
tag: RootTag, // 用来表示是否开启“并发模式”
options: void | RootOptions
) {
const root = createFiberRoot(
container,
tag,
(hydrate = false),
(hydrationCallbacks = null)
);
container.__reactContainer$ = root.current;
/**
* 获取应用挂载的 DOM 的 nodeType,nodeType 都有:
* Node.ELEMENT_NODE 1 元素节点
* Node.TEXT_NODE 3 文字
* Node.CDATA_SECTION_NODE 4 <!CDATA[[ … ]]>
* Node.PROCESSING_INSTRUCTION_NODE 7 <?xml-stylesheet ... ?>
* Node.COMMENT_NODE 8 注释
* Node.DOCUMENT_NODE 9 Document
* Node.DOCUMENT_TYPE_NODE 10 <!DOCTYPE html>
* Node.DOCUMENT_FRAGMENT_NODE 11 DocumentFragment
*/
const rootContainerElement =
container.nodeType === COMMENT_NODE ? container.parentNode : container;
rootContainerElement._reactListening = true;
/**
* allNativeEvents 是包含了离散(触发)事件、阻塞(用户交互)事件、连续(不能被打断)事件、表单事件的 Set
* nonDelegatedEvents 是放置的是“非委托”事件的 Set (类似于一个白名单)
* listenToNativeEvent(事件名称,是否在捕获阶段触发,应用根容器,目标元素)
* 1. 将 selectionchange 绑定到 document
* 2. 将支持“冒泡”的事件委托到 rootContainerElement
* 3. 其他的事件除了 scroll 外,绑定到“目标元素”
* 4. 从 EventTarget.__reactEvents 找到所有绑定的 listener(保存在 Set 里)
* 5. listener 里如果没有 `${domEventName}__${capture ? 'capture' : 'bubble'}` 会执行 addTrappedEventListener
* addTrappedEventListener(EventTarget, DOMEventName, EventSystemFlags, isCapture, isDeferred)
* 1. createEventListenerWrapperWithPriority 会根据事件类型,使用不同的优先级函数包裹 listener(事件优先级)
* a. dispatchDiscreteEvent 离散事件
* b. dispatchUserBlockingUpdate 用户阻塞事件
* c. dispatchEvent 连续事件
* 2. 对于“touchstart”、“touchmove”、“wheel”三个事件,isPassive=true(提前告知浏览器,我没有阻止默认行为,不要害怕)
* 3. 根据 isCapture 和 isPassive 将事件绑定分发给了四个函数 addEvent[Bubble/Capture]Listener[WithPassiveFlag]
* 4. 调用原生 API:addEventListener 和 removeEventListener 进行事件的绑定和移除
* 总的来说,React 会根据事件 isCapture、isPassive 和优先级,将事件绑定到合适的 target 上
*/
allNativeEvents.forEach(domEventName => {
if (!nonDelegatedEvents.has(domEventName)) {
// 可以使用“事件委托”
listenToNativeEvent(
domEventName,
false, // “事件委托”需要“事件冒泡”的支持
rootContainerElement,
null
);
}
listenToNativeEvent(domEventName, true, rootContainerElement, null);
});
return root;
}
// 创建 fiberRoot 和 rootFiberNode,并将两者关联起来
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean
): FiberRoot {
const root: FiberRoot = {
tag,
containerInfo, // DOMContainer
pendingChildren: null,
current: null,
pingCache: null,
finishedWork: null,
timeoutHandle: noTimeout, // -1
context: null,
pendingContext: null,
hydrate,
callbackNode: null,
callbackPriority: NoLanePriority, // 0
eventTimes: createLaneMap(NoLanes), // []
expirationTimes: createLaneMap(NoTimestamp),
pendingLanes: NoLanes,
suspendedLanes: NoLanes,
pingedLanes: NoLanes,
expiredLanes: NoLanes,
mutableReadLanes: NoLanes,
finishedLanes: NoLanes,
entangledLanes: NoLanes,
entanglements: createLaneMap(NoLanes)
};
const uninitializedFiber: FiberNode = {
tag: 3,
key: null,
elementType: null,
type: null,
stateNode: null,
return: null,
child: null,
sibling: null,
index: 0,
ref: null,
pendingProps: null,
memoizedProps: null,
updateQueue: null,
memoizedState: null,
dependencies: null,
/**
* mode 有六种模式:
* 0b00000 NoMode 普通模式
* 0b00001 StrictMode 严格模式
* 0b00010 BlockingMode 块模式
* 0b00100 ConcurrentMode 并发模式
* 0b01000 ProfileMode 性能测试模式
* 0b10000 DebugTracingMode Debug 模式
*/
mode: NoMode | ConcurrentMode | BlockingMode | StrictMode,
flags: NoFlags,
nextEffect: null,
firstEffect: null,
lastEffect: null,
lanes: NoLanes,
childLanes: NoLanes,
alternate: null
};
// fiberRoot 的 current 指向 rootFiberNode
root.current = uninitializedFiber;
// rootFiberNode 的 stateNode 指向 fiberRoot
uninitializedFiber.stateNode = root;
// rootFiberNode 的更新队列(链表)
uninitializedFiber.updateQueue = {
baseState: uninitializedFiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null
},
effects: null
};
return root;
}
// 创建更新,安排渲染
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function
): Lane {
const current = container.current; // rootFiberNode
const eventTime = requestEventTime(); // 当前时间
const lane = requestUpdateLane(current); // 优先级
const context = getContextForSubtree(parentComponent); // emptyContextObject = {}
if (container.context === null) {
container.context = context; // 走这儿
} else {
container.pendingContext = context;
}
// 创建了一个 update 对象
const update = {
callback: null,
eventTime,
lane,
next: null,
payload: { element },
tag: UpdateState // 0
};
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}
/**
* enqueueUpdate 做了一下事情:将 fiber 节点和 update 关联起来
* const updateQueue = fiber.updateQueue;
* const sharedQueue = fiber.updateQueue.shared;
* const pending = sharedQueue.pending;
* update.next = update; // 每个 fiberNode 的所有 update 组成了一个链表,只不过现在链表里只有一个元素
* sharedQueue.pending = update;
*/
enqueueUpdate(current, update);
// 执行渲染工作
scheduleUpdateOnFiber(current, lane, eventTime);
return lane;
}
/**
* 从 17 开始,e.persist() 将不再生效,因为 SyntheticEvent 不再放入事件池中
* React 将不再向 document 附加事件处理器
* 而会将事件处理器附加到渲染 React 树的根 DOM 容器中
* 原因:如果页面上有多个 React 版本,会破坏 e.stopPropagation()
*/
export function listenToNativeEvent(
domEventName: DOMEventName,
isCapturePhaseListener: boolean,
rootContainerElement: EventTarget,
targetElement: Element | null,
eventSystemFlags?: EventSystemFlags = 0
): void {
let target = rootContainerElement;
if (
domEventName === 'selectionchange' &&
rootContainerElement.nodeType !== DOCUMENT_NODE
) {
/**
* selectionchange 事件需要直接绑定在 document 上
* ownerDocument 返回节点的顶层的 document 对象
*/
target = rootContainerElement.ownerDocument;
}
/**
* 如果事件可以被委托(或者在捕获阶段触发)
* 那就注册在 rootContainerElement 上
* 否则,就绑定在 targetElement 上
*/
if (
targetElement !== null &&
!isCapturePhaseListener &&
nonDelegatedEvents.has(domEventName)
) {
/**
* 所有不能代理的事件(多媒体事件+cancel+close+invalid+load+scroll+toggle)
* 都绑定在 targetElement 上
*/
if (domEventName !== 'scroll') {
// 除了 scroll 事件,因为任何元素都可以滚动
return;
}
eventSystemFlags |= IS_NON_DELEGATED;
target = targetElement;
}
// target['__reactEvents$' + Math.random().toString(36).slice(2)] = new Set()
const listenerSet = getEventListenerSet(target);
// `${domEventName}__${isCapturePhaseListener ? 'capture' : 'bubble'}`
const listenerSetKey = getListenerSetKey(
domEventName,
isCapturePhaseListener
);
if (!listenerSet.has(listenerSetKey)) {
if (isCapturePhaseListener) {
eventSystemFlags |= IS_CAPTURE_PHASE;
}
addTrappedEventListener(
target,
domEventName,
eventSystemFlags,
isCapturePhaseListener
);
listenerSet.add(listenerSetKey);
}
}
|