作用
导出的类型
调用服务器回调
export type { CallServerCallback, EncodeFormActionCallback };
查找源映射 URL 回调
export type FindSourceMapURLCallback = (
fileName: string,
environmentName: string,
) => null | string;
调试通道回调
export type DebugChannelCallback = (message: string) => void;
调试通道
export type DebugChannel = {
hasReadable: boolean;
callback: DebugChannelCallback | null;
};
弱回应
// This indirection exists only to clean up DebugChannel when all Lazy References are GC:ed.
// Therefore we only use the indirection in DEV.
// 这种间接存在仅仅是为了在所有延迟引用被垃圾回收后清理 DebugChannel。
// 因此我们只在开发环境中使用这种间接。
type WeakResponse = {
weak: WeakRef<Response>;
// 当没有待处理的块时,这将为 null。
response: null | Response; // This is null when there are no pending chunks.
};
流状态
export type StreamState = {
_rowState: RowParserState;
// 到目前为止解析的行 ID 的部分
_rowID: number; // parts of a row ID parsed so far
// 0 表示我们当前正在解析行 ID
_rowTag: number; // 0 indicates that we're currently parsing the row ID
// 行中剩余的字节。0 表示我们正在寻找换行符。
_rowLength: number; // remaining bytes in the row. 0 indicates that we're looking for a newline.
// 到目前为止作为此行接收的块
_buffer: Array<Uint8Array>; // chunks received so far as part of this row
// 仅限开发
_debugInfo: ReactIOInfo; // DEV-only
// 仅限开发
_debugTargetChunkSize: number; // DEV-only
};
获取根
export function getRoot<T>(weakResponse: WeakResponse): Thenable<T> {
const response = unwrapWeakResponse(weakResponse);
const chunk = getChunk(response, 0);
return chunk as any;
}
报告全局错误
// Report that any missing chunks in the model is now going to throw this
// error upon read. Also notify any pending promises.
// 报告模型中任何缺失的数据块现在在读取时将抛出此错误。也通知任何待处理的承诺。
export function reportGlobalError(
weakResponse: WeakResponse,
error: Error,
): void {
if (hasGCedResponse(weakResponse)) {
// Ignore close signal if we are not awaiting any more pending chunks.
// 如果我们不再等待任何挂起的数据块,则忽略关闭信号。
return;
}
const response = unwrapWeakResponse(weakResponse);
response._closed = true;
response._closedReason = error;
response._chunks.forEach(chunk => {
// If this chunk was already resolved or errored, it won't
// trigger an error but if it wasn't then we need to
// because we won't be getting any new data to resolve it.
// 如果这个块已经被解决或出错,它不会触发错误,但如果还没有解决,则我们需要这样做
// 因为我们将不会收到任何新的数据来解决它。
if (chunk.status === PENDING) {
triggerErrorOnChunk(response, chunk, error);
} else if (chunk.status === INITIALIZED && chunk.reason !== null) {
chunk.reason.error(error);
}
});
if (__DEV__) {
const debugChannel = response._debugChannel;
if (debugChannel !== undefined) {
// If we don't have any more ways of reading data, we don't have to send
// any more neither. So we close the writable side.
// 如果我们没有更多读取数据的方式,我们也不必再发送更多数据。所以我们关闭可写端。
closeDebugChannel(debugChannel);
response._debugChannel = undefined;
// Make sure the debug channel is not closed a second time when the
// Response gets GC:ed.
// 确保调试通道在 Response 被垃圾回收时不会再次关闭。
if (debugChannelRegistry !== null) {
debugChannelRegistry.unregister(response);
}
}
}
}
创建响应
export function createResponse(
bundlerConfig: ServerConsumerModuleMap,
serverReferenceConfig: null | ServerManifest,
moduleLoading: ModuleLoading,
callServer: void | CallServerCallback,
encodeFormAction: void | EncodeFormActionCallback,
nonce: void | string,
temporaryReferences: void | TemporaryReferenceSet,
allowPartialStream: boolean,
findSourceMapURL: void | FindSourceMapURLCallback, // DEV-only
replayConsole: boolean, // DEV-only
environmentName: void | string, // DEV-only
debugStartTime: void | number, // DEV-only
debugEndTime: void | number, // DEV-only
debugChannel: void | DebugChannel, // DEV-only
): WeakResponse {
if (__DEV__) {
// We use eval to create fake function stacks which includes Component stacks.
// 我们使用 eval 来创建包含组件堆栈的假函数堆栈。
// A warning would be noise if you used Flight without Components and don't encounter
// errors. We're warning eagerly so that you configure your environment accordingly
// before you encounter an error.
// 如果你使用 Flight 而不使用组件且未遇到错误,警告可能会是噪音。我们提前发出警告,以便你在
// 遇到错误之前配置好你的环境。
checkEvalAvailabilityOnceDev();
}
return getWeakResponse(
new ResponseInstance(
bundlerConfig,
serverReferenceConfig,
moduleLoading,
callServer,
encodeFormAction,
nonce,
temporaryReferences,
allowPartialStream,
findSourceMapURL,
replayConsole,
environmentName,
debugStartTime,
debugEndTime,
debugChannel,
),
);
}
创建流状态
export function createStreamState(
weakResponse: WeakResponse, // DEV-only
streamDebugValue: mixed, // DEV-only
): StreamState {
const streamState: StreamState = {
_rowState: 0,
_rowID: 0,
_rowTag: 0,
_rowLength: 0,
_buffer: [],
} as Omit<StreamState, '_debugInfo' | '_debugTargetChunkSize'> as any;
if (__DEV__ && enableAsyncDebugInfo) {
const response = unwrapWeakResponse(weakResponse);
// Create an entry for the I/O to load the stream itself.
// 为 I/O 创建一个条目以加载流本身。
const debugValuePromise = Promise.resolve(streamDebugValue);
(debugValuePromise as any).status = 'fulfilled';
(debugValuePromise as any).value = streamDebugValue;
streamState._debugInfo = {
name: 'rsc stream',
start: response._debugStartTime,
// 完成一部分后将会更新
end: response._debugStartTime, // will be updated once we finish a chunk
// 将随着我们处理数据块而更新
byteSize: 0, // will be updated as we resolve a data chunk
value: debugValuePromise,
owner: response._debugRootOwner,
debugStack: response._debugRootStack,
debugTask: response._debugRootTask,
};
streamState._debugTargetChunkSize = MIN_CHUNK_SIZE;
}
return streamState;
}
处理二进制块
export function processBinaryChunk(
weakResponse: WeakResponse,
streamState: StreamState,
chunk: Uint8Array,
): void {
if (hasGCedResponse(weakResponse)) {
// Ignore more chunks if we've already GC:ed all listeners.
// 如果我们已经对所有监听器进行了垃圾回收,则忽略更多块。
return;
}
const response = unwrapWeakResponse(weakResponse);
let i = 0;
let rowState = streamState._rowState;
let rowID = streamState._rowID;
let rowTag = streamState._rowTag;
let rowLength = streamState._rowLength;
const buffer = streamState._buffer;
const chunkLength = chunk.length;
incrementChunkDebugInfo(streamState, chunkLength);
while (i < chunkLength) {
let lastIdx = -1;
switch (rowState) {
case ROW_ID: {
const byte = chunk[i++];
if (byte === 58 /* ":" */) {
// Finished the rowID, next we'll parse the tag.
// 完成了 rowID,接下来我们将解析标签。
rowState = ROW_TAG;
} else {
rowID = (rowID << 4) | (byte > 96 ? byte - 87 : byte - 48);
}
continue;
}
case ROW_TAG: {
const resolvedRowTag = chunk[i];
if (
resolvedRowTag === 84 /* "T" */ ||
resolvedRowTag === 65 /* "A" */ ||
resolvedRowTag === 79 /* "O" */ ||
resolvedRowTag === 111 /* "o" */ ||
resolvedRowTag === 98 /* "b" */ ||
resolvedRowTag === 85 /* "U" */ ||
resolvedRowTag === 83 /* "S" */ ||
resolvedRowTag === 115 /* "s" */ ||
resolvedRowTag === 76 /* "L" */ ||
resolvedRowTag === 108 /* "l" */ ||
resolvedRowTag === 71 /* "G" */ ||
resolvedRowTag === 103 /* "g" */ ||
resolvedRowTag === 77 /* "M" */ ||
resolvedRowTag === 109 /* "m" */ ||
resolvedRowTag === 86 /* "V" */
) {
rowTag = resolvedRowTag;
rowState = ROW_LENGTH;
i++;
} else if (
(resolvedRowTag > 64 && resolvedRowTag < 91) /* "A"-"Z" */ ||
resolvedRowTag === 35 /* "#" */ ||
resolvedRowTag === 114 /* "r" */ ||
resolvedRowTag === 120 /* "x" */
) {
rowTag = resolvedRowTag;
rowState = ROW_CHUNK_BY_NEWLINE;
i++;
} else {
rowTag = 0;
rowState = ROW_CHUNK_BY_NEWLINE;
// This was an unknown tag so it was probably part of the data.
// 这是一个未知的标签,所以它可能是数据的一部分。
}
continue;
}
case ROW_LENGTH: {
const byte = chunk[i++];
if (byte === 44 /* "," */) {
// Finished the rowLength, next we'll buffer up to that length.
// 完成了行长,接下来我们将缓冲到该长度。
rowState = ROW_CHUNK_BY_LENGTH;
} else {
rowLength = (rowLength << 4) | (byte > 96 ? byte - 87 : byte - 48);
}
continue;
}
case ROW_CHUNK_BY_NEWLINE: {
// We're looking for a newline
// 我们正在寻找一个换行符
lastIdx = chunk.indexOf(10 /* "\n" */, i);
break;
}
case ROW_CHUNK_BY_LENGTH: {
// We're looking for the remaining byte length
// 我们正在寻找剩余的字节长度
lastIdx = i + rowLength;
if (lastIdx > chunk.length) {
lastIdx = -1;
}
break;
}
}
const offset = chunk.byteOffset + i;
if (lastIdx > -1) {
// We found the last chunk of the row
// 我们找到了这一行的最后一块
const length = lastIdx - i;
const lastChunk = new Uint8Array(chunk.buffer, offset, length);
// Check if this is a Uint8Array for a byte stream. We enqueue it
// immediately but need to determine if we can use zero-copy or must copy.
// 检查这是否是用于字节流的 Uint8Array。我们会立即将其排入队列,但需要确定是否可以使用
// 零拷贝或必须拷贝。
if (rowTag === 98 /* "b" */) {
resolveBuffer(
response,
rowID,
// If we're at the end of the RSC chunk, no more parsing will access
// this buffer and we don't need to copy the chunk to allow detaching
// the buffer, otherwise we need to copy.
// 如果我们处于 RSC 块的末尾,将不会有更多的解析访问这个缓冲区,并且我们不需要复制
// 该块以允许分离缓冲区,否则我们需要复制。
lastIdx === chunkLength ? lastChunk : lastChunk.slice(),
streamState,
);
} else {
// Process all other row types.
// 处理所有其他类型的行。
processFullBinaryRow(
response,
streamState,
rowID,
rowTag,
buffer,
lastChunk,
);
}
// Reset state machine for a new row
i = lastIdx;
if (rowState === ROW_CHUNK_BY_NEWLINE) {
// If we're trailing by a newline we need to skip it.
// 如果在换行符之后,需要跳过它。
i++;
}
rowState = ROW_ID;
rowTag = 0;
rowID = 0;
rowLength = 0;
buffer.length = 0;
} else {
// The rest of this row is in a future chunk.
// 这一行的其余部分将在未来的块中。
const length = chunk.byteLength - i;
const remainingSlice = new Uint8Array(chunk.buffer, offset, length);
// For byte streams, we can enqueue the partial row immediately without
// copying since we're at the end of the RSC chunk and no more parsing
// will access this buffer.
// 对于字节流,我们可以立即将部分行入队而无需复制,因为我们已经在 RSC 块的末尾,并且不会
// 有更多解析访问这个缓冲区。
if (rowTag === 98 /* "b" */) {
// Update how many bytes we're still waiting for. We need to do this
// before enqueueing, as enqueue will detach the buffer and byteLength
// will become 0.
// 更新我们仍在等待的字节数。我们需要在入队之前执行此操作,因为入队会分离缓冲区,
// byteLength 会变为 0。
rowLength -= remainingSlice.byteLength;
resolveBuffer(response, rowID, remainingSlice, streamState);
} else {
// For other row types, stash the rest of the current chunk until we can
// process the full row.
// 对于其他行类型,将当前块的其余部分暂存,直到我们可以处理完整的行。
buffer.push(remainingSlice);
// Update how many bytes we're still waiting for. If we're looking for
// a newline, this doesn't hurt since we'll just ignore it.
// 更新仍在等待的字节数。如果在寻找换行符,这不会有问题,因为只是会忽略它。
rowLength -= remainingSlice.byteLength;
}
break;
}
}
streamState._rowState = rowState;
streamState._rowID = rowID;
streamState._rowTag = rowTag;
streamState._rowLength = rowLength;
}
处理字符串块
export function processStringChunk(
weakResponse: WeakResponse,
streamState: StreamState,
chunk: string,
): void {
if (hasGCedResponse(weakResponse)) {
// Ignore more chunks if we've already GC:ed all listeners.
// 如果我们已经对所有监听器进行了垃圾回收,则忽略更多块。
return;
}
const response = unwrapWeakResponse(weakResponse);
// This is a fork of processBinaryChunk that takes a string as input.
// 这是 processBinaryChunk 的一个分支,用于以字符串作为输入。
// This can't be just any binary chunk coverted to a string. It needs to be
// in the same offsets given from the Flight Server. E.g. if it's shifted by
// one byte then it won't line up to the UCS-2 encoding. It also needs to
// be valid Unicode. Also binary chunks cannot use this even if they're
// value Unicode. Large strings are encoded as binary and cannot be passed
// here. Basically, only if Flight Server gave you this string as a chunk,
// you can use it here.
// 这不能是任何被转换为字符串的二进制块。它需要与 Flight Server 给出的偏移量相同。例如,如
// 果偏移量偏移了一个字节,那么它将无法对齐到 UCS-2 编码。它还需要是有效的 Unicode。此外,
// 即使二进制块是有效的 Unicode,也不能使用这个方法。大字符串以二进制形式编码,不能传递到这
// 里。基本上,只有当 Flight Server 给你作为块提供了这个字符串时,才能在这里使用它。
let i = 0;
let rowState = streamState._rowState;
let rowID = streamState._rowID;
let rowTag = streamState._rowTag;
let rowLength = streamState._rowLength;
const buffer = streamState._buffer;
const chunkLength = chunk.length;
incrementChunkDebugInfo(streamState, chunkLength);
while (i < chunkLength) {
let lastIdx = -1;
switch (rowState) {
case ROW_ID: {
const byte = chunk.charCodeAt(i++);
if (byte === 58 /* ":" */) {
// Finished the rowID, next we'll parse the tag.
// 完成了 rowID,接下来我们将解析标签。
rowState = ROW_TAG;
} else {
rowID = (rowID << 4) | (byte > 96 ? byte - 87 : byte - 48);
}
continue;
}
case ROW_TAG: {
const resolvedRowTag = chunk.charCodeAt(i);
if (
resolvedRowTag === 84 /* "T" */ ||
resolvedRowTag === 65 /* "A" */ ||
resolvedRowTag === 79 /* "O" */ ||
resolvedRowTag === 111 /* "o" */ ||
resolvedRowTag === 85 /* "U" */ ||
resolvedRowTag === 83 /* "S" */ ||
resolvedRowTag === 115 /* "s" */ ||
resolvedRowTag === 76 /* "L" */ ||
resolvedRowTag === 108 /* "l" */ ||
resolvedRowTag === 71 /* "G" */ ||
resolvedRowTag === 103 /* "g" */ ||
resolvedRowTag === 77 /* "M" */ ||
resolvedRowTag === 109 /* "m" */ ||
resolvedRowTag === 86 /* "V" */
) {
rowTag = resolvedRowTag;
rowState = ROW_LENGTH;
i++;
} else if (
(resolvedRowTag > 64 && resolvedRowTag < 91) /* "A"-"Z" */ ||
resolvedRowTag === 114 /* "r" */ ||
resolvedRowTag === 120 /* "x" */
) {
rowTag = resolvedRowTag;
rowState = ROW_CHUNK_BY_NEWLINE;
i++;
} else {
rowTag = 0;
rowState = ROW_CHUNK_BY_NEWLINE;
// This was an unknown tag so it was probably part of the data.
// 这是一个未知的标签,所以它可能是数据的一部分。
}
continue;
}
case ROW_LENGTH: {
const byte = chunk.charCodeAt(i++);
if (byte === 44 /* "," */) {
// Finished the rowLength, next we'll buffer up to that length.
// 完成了 rowLength,接下来我们将缓冲到该长度。
rowState = ROW_CHUNK_BY_LENGTH;
} else {
rowLength = (rowLength << 4) | (byte > 96 ? byte - 87 : byte - 48);
}
continue;
}
case ROW_CHUNK_BY_NEWLINE: {
// We're looking for a newline
// 我们正在寻找一个换行符
lastIdx = chunk.indexOf('\n', i);
break;
}
case ROW_CHUNK_BY_LENGTH: {
if (rowTag !== 84) {
throw new Error(
'Binary RSC chunks cannot be encoded as strings. ' +
'This is a bug in the wiring of the React streams.',
);
}
// For a large string by length, we don't know how many unicode characters
// we are looking for but we can assume that the raw string will be its own
// chunk. We add extra validation that the length is at least within the
// possible byte range it could possibly be to catch mistakes.
// 对于按长度定义的大字符串,不知道要找多少个 Unicode 字符,但仍可以假设原始字符串将
// 作为它自己的块存在。添加额外的验证,确保长度至少在可能的字节范围内,以捕捉错误。
if (rowLength < chunk.length || chunk.length > rowLength * 3) {
throw new Error(
'String chunks need to be passed in their original shape. ' +
'Not split into smaller string chunks. ' +
'This is a bug in the wiring of the React streams.',
);
}
lastIdx = chunk.length;
break;
}
}
if (lastIdx > -1) {
// We found the last chunk of the row
// 已找到了这一行的最后一块
if (buffer.length > 0) {
// If we had a buffer already, it means that this chunk was split up into
// binary chunks preceeding it.
// 如果当前已经有一个缓冲区,这意味着这个块已经被拆分成了它之前的二进制块。
throw new Error(
'String chunks need to be passed in their original shape. ' +
'Not split into smaller string chunks. ' +
'This is a bug in the wiring of the React streams.',
);
}
const lastChunk = chunk.slice(i, lastIdx);
processFullStringRow(response, streamState, rowID, rowTag, lastChunk);
// Reset state machine for a new row
// 为新的一行重置状态机
i = lastIdx;
if (rowState === ROW_CHUNK_BY_NEWLINE) {
// If we're trailing by a newline we need to skip it.
// 如果在换行符之后,则需要跳过它。
i++;
}
rowState = ROW_ID;
rowTag = 0;
rowID = 0;
rowLength = 0;
buffer.length = 0;
} else if (chunk.length !== i) {
// The rest of this row is in a future chunk. We only support passing the
// string from chunks in their entirety. Not split up into smaller string chunks.
// We could support this by buffering them but we shouldn't need to for
// this use case.
// 这一行的其余部分在未来的块中。只支持传递整个块的字符串,而不是分成更小的字符串块。
// 我们可以通过缓冲它们来支持这一点,但对于这个用例,我们不需要这样做。
throw new Error(
'String chunks need to be passed in their original shape. ' +
'Not split into smaller string chunks. ' +
'This is a bug in the wiring of the React streams.',
);
}
}
streamState._rowState = rowState;
streamState._rowID = rowID;
streamState._rowTag = rowTag;
streamState._rowLength = rowLength;
}
关闭
export function close(weakResponse: WeakResponse): void {
// In case there are any remaining unresolved chunks, they won't be resolved
// now. So we either error or halt them depending on whether partial streams
// are allowed.
// 若还有任何未解决的块,它们不会被解决。因此要么报错,要么停止它们,取决于是否允许部分流
// TODO: Ideally we should be able to bail out early if we kept a ref count of
// pending chunks.
// TODO:理想情况下,如果我们保持了待处理块的引用计数,应该能够提前退出。
if (hasGCedResponse(weakResponse)) {
return;
}
const response = unwrapWeakResponse(weakResponse);
if (response._allowPartialStream) {
// For partial streams, we halt pending chunks instead of erroring them.
// 对于部分流,我们暂停等待的块而不是报错它们。
response._closed = true;
response._chunks.forEach(chunk => {
if (chunk.status === PENDING) {
// Clear listeners to release closures and transition to HALTED.
// Future .then() calls on HALTED chunks are no-ops.
// 清除监听器以释放闭包并过渡到 HALTED 状态。
// 对 HALTED 块的未来 .then() 调用将是无操作的。
releasePendingChunk(response, chunk);
const haltedChunk: HaltedChunk<any> = chunk as any;
haltedChunk.status = HALTED;
haltedChunk.value = null;
haltedChunk.reason = null;
} else if (chunk.status === INITIALIZED && chunk.reason !== null) {
// Stream chunk - close gracefully instead of erroring.
// 流块 - 优雅关闭而不是出错。
chunk.reason.close('"$undefined"');
}
});
if (__DEV__) {
const debugChannel = response._debugChannel;
if (debugChannel !== undefined) {
closeDebugChannel(debugChannel);
response._debugChannel = undefined;
if (debugChannelRegistry !== null) {
debugChannelRegistry.unregister(response);
}
}
}
} else {
reportGlobalError(weakResponse, new Error('Connection closed.'));
}
}
注入到开发者工具
export function injectIntoDevTools(): boolean {
const internals: Object = {
bundleType: __DEV__ ? 1 : 0, // Might add PROFILE later.
version: rendererVersion,
rendererPackageName: rendererPackageName,
currentDispatcherRef: ReactSharedInternals,
// Enables DevTools to detect reconciler version rather than renderer version
// which may not match for third party renderers.
// 允许 DevTools 检测协调器版本,而不是渲染器版本。对于第三方渲染器,这两个版本可能不匹配
reconcilerVersion: ReactVersion,
getCurrentComponentInfo: getCurrentOwnerInDEV,
};
return injectInternals(internals);
}
常量
React 共享内部服务器
备注
源码中 114 - 123 行
// TODO: This is an unfortunate hack. We shouldn't feature detect the internals
// like this. It's just that for now we support the same build of the Flight
// client both in the RSC environment, in the SSR environments as well as the
// browser client. We should probably have a separate RSC build. This is DEV
// only though.
// TODO: 这是一个不幸的应急方法。我们不应该像这样检测内部特性。
// 只是目前我们在 RSC 环境、SSR 环境以及浏览器客户端中都支持相同的 Flight 客户端构建。
// 我们可能应该有一个单独的 RSC 构建。不过这仅限于开发环境。
// React 共享内部服务器
const ReactSharedInteralsServer: void | SharedStateServer = (React as any)
.__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
// React共享内部
const ReactSharedInternals: SharedStateServer | SharedStateClient =
React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ||
ReactSharedInteralsServer;
行 ID
备注
源码中 142 - 146 行
// 行 ID
const ROW_ID = 0;
// 行标签
const ROW_TAG = 1;
// 行长度
const ROW_LENGTH = 2;
// 按换行符分块
const ROW_CHUNK_BY_NEWLINE = 3;
// 按长度分行块
const ROW_CHUNK_BY_LENGTH = 4;
待定
备注
源码中 150 - 158 行
// 待定
const PENDING = 'pending';
// 已阻止
const BLOCKED = 'blocked';
// 已解析模型
const RESOLVED_MODEL = 'resolved_model';
// 已解析模块
const RESOLVED_MODULE = 'resolved_module';
// 已初始化
const INITIALIZED = 'fulfilled';
// 出错
const ERRORED = 'rejected';
// 停止
const HALTED = 'halted'; // DEV-only. Means it never resolves even if connection closes.
// 原型
const __PROTO__ = '__proto__';
调试通道注册表
备注
源码中 415 - 419 行
// If FinalizationRegistry doesn't exist, we cannot use the debugChannel.
// 如果 FinalizationRegistry 不存在,我们就无法使用 debugChannel。
const debugChannelRegistry =
__DEV__ && typeof FinalizationRegistry === 'function'
? new FinalizationRegistry(closeDebugChannel)
: null;
可能有静态构造函数
备注
源码中 2322 行
const mightHaveStaticConstructor = /\bclass\b.*\bstatic\b/;
最小块大小
备注
源码中 2880 - 2888 行
// Depending on set up the chunks of a TLS connection can vary in size. However in practice it's often
// at 64kb or even multiples of 64kb. It can also be smaller but in practice it also happens that 64kb
// is around what you can download on fast 4G connection in 300ms which is what we throttle reveals at
// anyway. The net effect is that in practice, you won't really reveal anything in smaller units than
// 64kb if they're revealing at maximum speed in production. Therefore we group smaller chunks into
// these larger chunks since in production that's more realistic.
// 根据设置,TLS 连接的数据块大小可能会有所不同。然而在实际情况中,它通常是 64kb,或者是 64kb
// 的倍数。它也可以更小,但在实践中,64kb 大约是你在快速 4G 连接上 300 毫秒内可以下载的量,而这
// 也正是我们限制 reveals 的速度。不论如何,实际效果是,如果它们在生产环境中以最大速度揭示,你实
// 际上不会以小于 64kb 的单位泄露任何信息。因此,我们将较小的数据块合并成这些较大的块,因为在生
// 产环境中这样更符合实际。
// TODO: If the stream is compressed, then you could fit much more in a single 300ms so maybe it should
// actually be larger.
// TODO:如果流是压缩的,那么你可以在单个 300 毫秒内传输更多内容,因此它可能实际上应该更大。
const MIN_CHUNK_SIZE = 65536;
支持创建任务
备注
源码中 3646 行
const supportsCreateTask = __DEV__ && !!(console: any).createTask;
模拟函数
备注
源码中 3649 - 3651 行
const fakeFunctionCache: Map<string, FakeFunction<any>> = __DEV__
? new Map()
: (null as any);
创建模拟 JSX 调用堆栈
备注
源码中 3979 - 3994 行
const createFakeJSXCallStack = {
react_stack_bottom_frame: function (
response: Response,
stack: ReactStackTrace,
environmentName: string,
): Error {
const callStackForError = buildFakeCallStack(
response,
stack,
environmentName,
false,
fakeJSXCallSite,
);
return callStackForError();
},
};
在开发环境中创建模拟 JSX 调用堆栈
备注
源码中 3996 - 4005 行
const createFakeJSXCallStackInDEV: (
response: Response,
stack: ReactStackTrace,
environmentName: string,
) => Error = __DEV__
? // We use this technique to trick minifiers to preserve the function name.
// 我们使用这种技术来欺骗压缩器以保留函数名称。
(createFakeJSXCallStack.react_stack_bottom_frame.bind(
createFakeJSXCallStack,
) as any)
: (null as any);
使用调用栈重放控制台
备注
源码中 4153 - 4197 行
const replayConsoleWithCallStack = {
react_stack_bottom_frame: function (
response: Response,
payload: ConsoleEntry,
): void {
const methodName = payload[0];
const stackTrace = payload[1];
const owner = payload[2];
const env = payload[3];
const args = payload.slice(4);
// There really shouldn't be anything else on the stack atm.
// 目前栈上真的不应该还有其他东西。
const prevStack = ReactSharedInternals.getCurrentStack;
ReactSharedInternals.getCurrentStack = getCurrentStackInDEV;
currentOwnerInDEV =
owner === null ? (response._debugRootOwner as any) : owner;
try {
const callStack = buildFakeCallStack(
response,
stackTrace,
env,
false,
bindToConsole(methodName, args, env),
);
if (owner != null) {
const task = initializeFakeTask(response, owner);
initializeFakeStack(response, owner);
if (task !== null) {
task.run(callStack);
return;
}
}
const rootTask = getRootTask(response, env);
if (rootTask != null) {
rootTask.run(callStack);
return;
}
callStack();
} finally {
currentOwnerInDEV = null;
ReactSharedInternals.getCurrentStack = prevStack;
}
},
};
在开发环境中使用调用栈重放控制台
备注
源码中 4199 - 4207 行
const replayConsoleWithCallStackInDEV: (
response: Response,
payload: ConsoleEntry,
) => void = __DEV__
? // We use this technique to trick minifiers to preserve the function name.
// 我们使用这种技术来欺骗压缩器以保留函数名称。
(replayConsoleWithCallStack.react_stack_bottom_frame.bind(
replayConsoleWithCallStack,
) as any)
: (null as any);
变量
初始化处理器
备注
源码中 936 - 938 行
// 初始化处理器
let initializingHandler: null | InitializationHandler = null;
// 初始化块
let initializingChunk: null | BlockedChunk<any> = null;
// 正在初始化调试信息
let isInitializingDebugInfo: boolean = false;
模拟函数下标
备注
源码中 3653 行
let fakeFunctionIdx = 0;
当前开发者所有者
备注
源码中 4141 行
let currentOwnerInDEV: null | ReactComponentInfo = null;
工具
React 异步承诺
备注
enableProfilerTimer由 ReactFeatureFlags#enableProfilerTimer 提供enableComponentPerformanceTrack由 ReactFeatureFlags#enableComponentPerformanceTrack 提供enableAsyncDebugInfo由 ReactFeatureFlags#enableAsyncDebugInfo 提供
function ReactPromise(status: any, value: any, reason: any) {
this.status = status;
this.value = value;
this.reason = reason;
if (enableProfilerTimer && enableComponentPerformanceTrack) {
this._children = [];
}
if (__DEV__) {
this._debugChunk = null;
this._debugInfo = [];
}
}
// We subclass Promise.prototype so that we get other methods like .catch
// 我们对 Promise.prototype 进行子类化,这样我们就可以获得其他方法,比如 .catch
ReactPromise.prototype = Object.create(Promise.prototype) as any;
// TODO: This doesn't return a new Promise chain unlike the real .then
// 待办事项:这不会像真正的 .then 那样返回一个新的 Promise 链
ReactPromise.prototype.then = function <T>(
this: SomeChunk<T>,
resolve: (value: T) => mixed,
reject?: (reason: mixed) => mixed,
) {
const chunk: SomeChunk<T> = this;
// If we have resolved content, we try to initialize it first which
// might put us back into one of the other states.
// 如果我们已经解析了内容,我们会尝试先初始化它,这可能会把我们带回到其他状态之一。
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(chunk);
break;
}
if (__DEV__ && enableAsyncDebugInfo) {
// Because only native Promises get picked up when we're awaiting we need to wrap
// this in a native Promise in DEV. This means that these callbacks are no longer sync
// but the lazy initialization is still sync and the .value can be inspected after,
// allowing it to be read synchronously anyway.
// 因为只有原生的 Promise 在我们使用 await 时才会被捕获,所以我们需要在开发环境中用原生 Promise 包装它。
// 这意味着这些回调不再是同步的,但延迟初始化仍然是同步的,并且 .value 之后可以被检查,
// 无论如何都可以同步读取它。
const resolveCallback = resolve;
const rejectCallback = reject;
const wrapperPromise: Promise<T> = new Promise((res, rej) => {
resolve = value => {
wrapperPromise._debugInfo = this._debugInfo;
res(value);
};
reject = reason => {
wrapperPromise._debugInfo = this._debugInfo;
rej(reason);
};
});
wrapperPromise.then(resolveCallback, rejectCallback);
}
// The status might have changed after initialization.
// 状态可能在初始化后发生变化。
switch (chunk.status) {
case INITIALIZED:
if (typeof resolve === 'function') {
resolve(chunk.value);
}
break;
case PENDING:
case BLOCKED:
if (typeof resolve === 'function') {
if (chunk.value === null) {
chunk.value = [] as Array<
InitializationReference | ((t: T) => mixed)
>;
}
chunk.value.push(resolve);
}
if (typeof reject === 'function') {
if (chunk.reason === null) {
chunk.reason = [] as Array<
InitializationReference | ((mixed: mixed) => mixed)
>;
}
chunk.reason.push(reject);
}
break;
case HALTED: {
break;
}
default:
if (typeof reject === 'function') {
reject(chunk.reason);
}
break;
}
};
已回收响应
function hasGCedResponse(weakResponse: WeakResponse): boolean {
return __DEV__ && weakResponse.weak.deref() === undefined;
}
展开弱响应
function unwrapWeakResponse(weakResponse: WeakResponse): Response {
if (__DEV__) {
const response = weakResponse.weak.deref();
if (response === undefined) {
throw new Error(
'We did not expect to receive new data after GC:ing the response.',
);
}
return response;
} else {
// 在生产环境中,我们直接使用真实的 Response。
return weakResponse as any; // In prod we just use the real Response directly.
}
}
获得弱响应
function getWeakResponse(response: Response): WeakResponse {
if (__DEV__) {
return response._weakResponse;
} else {
// 在生产环境中,我们直接使用真实的 Response。
return response as any; // In prod we just use the real Response directly.
}
}
关闭调试通道
function closeDebugChannel(debugChannel: DebugChannel): void {
if (debugChannel.callback) {
debugChannel.callback('');
}
}
读取块
function readChunk<T>(chunk: SomeChunk<T>): T {
// If we have resolved content, we try to initialize it first which
// might put us back into one of the other states.
// 如果我们有已解析的内容,我们会尝试先初始化它,这可能会使我们回到其他状态之一。
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(chunk);
break;
}
// The status might have changed after initialization.
// 状态可能在初始化后发生变化。
switch (chunk.status) {
case INITIALIZED:
return chunk.value;
case PENDING:
case BLOCKED:
case HALTED:
throw chunk as any as Thenable<T>;
default:
throw chunk.reason;
}
}
创建未完成块
function createPendingChunk<T>(response: Response): PendingChunk<T> {
if (__DEV__) {
// Retain a strong reference to the Response while we wait for the result.
// 在等待结果的同时保持对 Response 的强引用。
if (response._pendingChunks++ === 0) {
response._weakResponse.response = response;
if (response._pendingInitialRender !== null) {
clearTimeout(response._pendingInitialRender);
response._pendingInitialRender = null;
}
}
}
return new ReactPromise(PENDING, null, null);
}
释放未完成块
function releasePendingChunk(response: Response, chunk: SomeChunk<any>): void {
if (__DEV__ && chunk.status === PENDING) {
if (--response._pendingChunks === 0) {
// We're no longer waiting for any more chunks. We can release the strong reference
// to the response. We'll regain it if we ask for any more data later on.
// 我们不再等待更多的数据块。我们可以释放对响应的强引用。如果我们以后请求更多数
// 据,我们将重新获取它。
response._weakResponse.response = null;
// Wait a short period to see if any more chunks get asked for. E.g. by a React render.
// 等待一小段时间,看看是否有更多的块被请求。例如,通过 React 渲染。
// These chunks might discover more pending chunks.
// 这些块可能会发现更多待处理的块。
// If we don't ask for more then we assume that those chunks weren't blocking initial
// render and are excluded from the performance track.
// 如果我们不请求更多块,那么我们假设那些块并未阻塞初始渲染,因此被排除在性能跟踪之外。
response._pendingInitialRender = setTimeout(
flushInitialRenderPerformance.bind(null, response),
100,
);
}
}
}
创建阻塞块
function createBlockedChunk<T>(response: Response): BlockedChunk<T> {
return new ReactPromise(BLOCKED, null, null);
}
创建错误块
function createErrorChunk<T>(
response: Response,
error: mixed,
): ErroredChunk<T> {
return new ReactPromise(ERRORED, null, error);
}
过滤调试信息
function filterDebugInfo(
response: Response,
// value: {_debugInfo: ReactDebugInfo, ...},
value: { _debugInfo: ReactDebugInfo },
) {
if (response._debugEndTime === null) {
// No end time was defined, so we keep all debug info entries.
// 未定义结束时间,因此我们保留所有调试信息条目。
return;
}
// Remove any debug info entries after the defined end time. For async info
// that means we're including anything that was awaited before the end time,
// but it doesn't need to be resolved before the end time.
// 在定义的结束时间之后移除任何调试信息条目。对于异步信息。这意味着我们包含在结束时间之
// 前等待的任何内容,但是它不需要在结束时间之前被解析。
const relativeEndTime = response._debugEndTime - performance.timeOrigin;
const debugInfo = [];
for (let i = 0; i < value._debugInfo.length; i++) {
const info = value._debugInfo[i];
if (typeof info.time === 'number' && info.time > relativeEndTime) {
break;
}
debugInfo.push(info);
}
value._debugInfo = debugInfo;
}
将调试信息从块移动到内部值
备注
isArray由 isArray 实现
function moveDebugInfoFromChunkToInnerValue<T>(
chunk: InitializedChunk<T> | InitializedStreamChunk<any>,
value: T,
): void {
// Remove the debug info from the initialized chunk, and add it to the inner
// value instead. This can be a React element, an array, or an uninitialized
// Lazy.
// 从已初始化的块中移除调试信息,并将其添加到内部值中。
// 这可以是一个 React 元素、一个数组,或一个未初始化的 Lazy。
const resolvedValue = resolveLazy(value);
if (
typeof resolvedValue === 'object' &&
resolvedValue !== null &&
(isArray(resolvedValue) ||
typeof resolvedValue[ASYNC_ITERATOR] === 'function' ||
resolvedValue.$$typeof === REACT_ELEMENT_TYPE ||
resolvedValue.$$typeof === REACT_LAZY_TYPE)
) {
const debugInfo = chunk._debugInfo.splice(0);
if (isArray(resolvedValue._debugInfo)) {
resolvedValue._debugInfo.unshift.apply(
resolvedValue._debugInfo,
debugInfo,
);
} else if (!Object.isFrozen(resolvedValue)) {
Object.defineProperty(resolvedValue as any, '_debugInfo', {
configurable: false,
enumerable: false,
writable: true,
value: debugInfo,
});
}
// TODO: If the resolved value is a frozen element (e.g. a client-created
// element from a temporary reference, or a JSX element exported as a client
// reference), server debug info is currently dropped because the element
// can't be mutated. We should probably clone the element so each rendering
// context gets its own mutable copy with the correct debug info.
// TODO: 如果解析后的值是一个冻结的元素(例如从临时引用创建的客户端元素,或作为客户
// 端引用导出的 JSX 元素),服务器调试信息目前会丢失,因为该元素无法被修改。我们可
// 能需要克隆该元素,以便每个渲染上下文都能获得自己的可修改副本,并带有正确的调试信息。
}
}
处理块调试信息
function processChunkDebugInfo<T>(
response: Response,
chunk: InitializedChunk<T> | InitializedStreamChunk<any>,
value: T,
): void {
filterDebugInfo(response, chunk);
moveDebugInfoFromChunkToInnerValue(chunk, value);
}
唤醒块
function wakeChunk<T>(
response: Response,
// listeners: Array<InitializationReference | (T => mixed)>,
listeners: Array<InitializationReference | ((t: T) => mixed)>,
value: T,
chunk: InitializedChunk<T>,
): void {
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
if (typeof listener === 'function') {
listener(value);
} else {
fulfillReference(response, listener, value, chunk);
}
}
if (__DEV__) {
processChunkDebugInfo(response, chunk, value);
}
}
拒绝块
function rejectChunk(
response: Response,
// listeners: Array<InitializationReference | (mixed => mixed)>,
listeners: Array<InitializationReference | ((mixed: mixed) => mixed)>,
error: mixed,
): void {
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
if (typeof listener === 'function') {
listener(error);
} else {
rejectReference(response, listener.handler, error);
}
}
}
解决阻塞循环
function resolveBlockedCycle<T>(
resolvedChunk: SomeChunk<T>,
reference: InitializationReference,
): null | InitializationHandler {
const referencedChunk = reference.handler.chunk;
if (referencedChunk === null) {
return null;
}
if (referencedChunk === resolvedChunk) {
// We found the cycle. We can resolve the blocked cycle now.
// 我们找到了循环。我们现在可以解决阻塞的循环。
return reference.handler;
}
const resolveListeners = referencedChunk.value;
if (resolveListeners !== null) {
for (let i = 0; i < resolveListeners.length; i++) {
const listener = resolveListeners[i];
if (typeof listener !== 'function') {
const foundHandler = resolveBlockedCycle(resolvedChunk, listener);
if (foundHandler !== null) {
return foundHandler;
}
}
}
}
return null;
}
如果已初始化则唤醒块
function wakeChunkIfInitialized<T>(
response: Response,
chunk: SomeChunk<T>,
// resolveListeners: Array<InitializationReference | (T => mixed)>,
resolveListeners: Array<InitializationReference | ((t: T) => mixed)>,
// rejectListeners: null | Array<InitializationReference | (mixed => mixed)>,
rejectListeners: null | Array<
InitializationReference | ((mixed: mixed) => mixed)
>,
): void {
switch (chunk.status) {
case INITIALIZED:
wakeChunk(response, resolveListeners, chunk.value, chunk);
break;
case BLOCKED:
// It is possible that we're blocked on our own chunk if it's a cycle.
// Before adding back the listeners to the chunk, let's check if it would
// result in a cycle.
// 如果我们的块本身形成了循环,可能会被阻塞。
// 在将监听器重新添加到块之前,让我们检查一下是否会导致循环。
for (let i = 0; i < resolveListeners.length; i++) {
const listener = resolveListeners[i];
if (typeof listener !== 'function') {
const reference: InitializationReference = listener;
const cyclicHandler = resolveBlockedCycle(chunk, reference);
if (cyclicHandler !== null) {
// This reference points back to this chunk. We can resolve the cycle by
// using the value from that handler.
// 这个引用指回这个块。我们可以通过使用该处理程序的值来解决循环。
fulfillReference(response, reference, cyclicHandler.value, chunk);
resolveListeners.splice(i, 1);
i--;
if (rejectListeners !== null) {
const rejectionIdx = rejectListeners.indexOf(reference);
if (rejectionIdx !== -1) {
rejectListeners.splice(rejectionIdx, 1);
}
}
// The status might have changed after fulfilling the reference.
// 在满足参考后,状态可能已经改变。
switch ((chunk as SomeChunk<T>).status) {
case INITIALIZED:
const initializedChunk: InitializedChunk<T> = chunk as any;
wakeChunk(
response,
resolveListeners,
initializedChunk.value,
initializedChunk,
);
return;
case ERRORED:
if (rejectListeners !== null) {
rejectChunk(response, rejectListeners, chunk.reason);
}
return;
}
}
}
}
// Fallthrough
// 穿透
case PENDING:
if (chunk.value) {
for (let i = 0; i < resolveListeners.length; i++) {
chunk.value.push(resolveListeners[i]);
}
} else {
chunk.value = resolveListeners;
}
if (chunk.reason) {
if (rejectListeners) {
for (let i = 0; i < rejectListeners.length; i++) {
chunk.reason.push(rejectListeners[i]);
}
}
} else {
chunk.reason = rejectListeners;
}
break;
case ERRORED:
if (rejectListeners) {
rejectChunk(response, rejectListeners, chunk.reason);
}
break;
}
}
在块上触发错误
function triggerErrorOnChunk<T>(
response: Response,
chunk: SomeChunk<T>,
error: mixed,
): void {
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
// If we get more data to an already resolved ID, we assume that it's
// a stream chunk since any other row shouldn't have more than one entry.
// 如果我们获取到已解析 ID 的更多数据,我们假设它是
// 一个流块,因为任何其他行不应该有超过一个条目。
const streamChunk: InitializedStreamChunk<any> = chunk as any;
const controller = streamChunk.reason;
controller.error(error);
return;
}
releasePendingChunk(response, chunk);
const listeners = chunk.reason;
if (__DEV__ && chunk.status === PENDING) {
// Lazily initialize any debug info and block the initializing chunk on any unresolved entries.
// 延迟初始化任何调试信息,并在存在未解析条目时阻塞初始化块。
if (chunk._debugChunk != null) {
const prevHandler = initializingHandler;
const prevChunk = initializingChunk;
initializingHandler = null;
const cyclicChunk: BlockedChunk<T> = chunk as any;
cyclicChunk.status = BLOCKED;
cyclicChunk.value = null;
cyclicChunk.reason = null;
if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {
initializingChunk = cyclicChunk;
}
try {
initializeDebugChunk(response, chunk);
if (initializingHandler !== null) {
if (initializingHandler.errored) {
// Ignore error parsing debug info, we'll report the original error instead.
// 忽略解析调试信息时的错误,我们将改为报告原始错误。
} else if (initializingHandler.deps > 0) {
// TODO: Block the resolution of the error until all the debug info has loaded.
// We currently don't have a way to throw an error after all dependencies have
// loaded because we currently treat errors as immediately cancelling the handler.
// TODO: 在所有调试信息加载完成之前阻止错误的解析。
// 我们目前没有在所有依赖项加载完成后抛出错误的方法,
// 因为我们目前将错误视为立即取消处理程序。
}
}
} finally {
initializingHandler = prevHandler;
initializingChunk = prevChunk;
}
}
}
const erroredChunk: ErroredChunk<T> = chunk as any;
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
if (listeners !== null) {
rejectChunk(response, listeners, error);
}
}
创建已解析模型块
function createResolvedModelChunk<T>(
response: Response,
value: UninitializedModel,
): ResolvedModelChunk<T> {
return new ReactPromise(RESOLVED_MODEL, value, response);
}
创建已解析模块块
function createResolvedModuleChunk<T>(
response: Response,
value: ClientReference<T>,
): ResolvedModuleChunk<T> {
return new ReactPromise(RESOLVED_MODULE, value, null);
}
创建已初始化的文本块
function createInitializedTextChunk(
response: Response,
value: string,
): InitializedChunk<string> {
return new ReactPromise(INITIALIZED, value, null);
}
创建已初始化缓冲区块
function createInitializedBufferChunk(
response: Response,
value: $ArrayBufferView | ArrayBuffer,
): InitializedChunk<Uint8Array> {
return new ReactPromise(INITIALIZED, value, null);
}
创建已初始化的迭代器结果块
function createInitializedIteratorResultChunk<T>(
response: Response,
value: T,
done: boolean,
): InitializedChunk<IteratorResult<T, T>> {
return new ReactPromise(INITIALIZED, { done: done, value: value }, null);
}
创建已初始化的流块
function createInitializedStreamChunk<
T extends ReadableStream | $AsyncIterable<any, any, void>,
>(
response: Response,
value: T,
controller: FlightStreamController,
): InitializedChunk<T> {
if (__DEV__) {
// Retain a strong reference to the Response while we wait for chunks.
// 在我们等待数据块时保持对 Response 的强引用。
if (response._pendingChunks++ === 0) {
response._weakResponse.response = response;
}
}
// We use the reason field to stash the controller since we already have that
// field. It's a bit of a hack but efficient.
// 我们使用 reason 字段来存储控制器,因为我们已经有了这个字段。
// 这有点像是一个小技巧,但很高效。
return new ReactPromise(INITIALIZED, value, controller);
}
创建已解析的迭代器结果块
function createResolvedIteratorResultChunk<T>(
response: Response,
value: UninitializedModel,
done: boolean,
): ResolvedModelChunk<IteratorResult<T, T>> {
// To reuse code as much code as possible we add the wrapper element as part of the JSON.
// 为了尽可能多地重用代码,我们将包装元素作为 JSON 的一部分添加。
const iteratorResultJSON =
(done ? '{"done":true,"value":' : '{"done":false,"value":') + value + '}';
return new ReactPromise(RESOLVED_MODEL, iteratorResultJSON, response);
}
解析迭代器结果块
function resolveIteratorResultChunk<T>(
response: Response,
chunk: SomeChunk<IteratorResult<T, T>>,
value: UninitializedModel,
done: boolean,
): void {
// To reuse code as much code as possible we add the wrapper element as part of the JSON.
// 为了尽可能多地重用代码,我们将包装元素作为 JSON 的一部分添加。
const iteratorResultJSON =
(done ? '{"done":true,"value":' : '{"done":false,"value":') + value + '}';
resolveModelChunk(response, chunk, iteratorResultJSON);
}
解析模型块
function resolveModelChunk<T>(
response: Response,
chunk: SomeChunk<T>,
value: UninitializedModel,
): void {
if (chunk.status !== PENDING) {
// If we get more data to an already resolved ID, we assume that it's
// a stream chunk since any other row shouldn't have more than one entry.
// 如果我们向一个已解析的 ID 获取更多数据,我们假设这是一个流块,因为任何其他行不
// 应该有多于一个的条目。
const streamChunk: InitializedStreamChunk<any> = chunk as any;
const controller = streamChunk.reason;
controller.enqueueModel(value);
return;
}
releasePendingChunk(response, chunk);
const resolveListeners = chunk.value;
const rejectListeners = chunk.reason;
const resolvedChunk: ResolvedModelChunk<T> = chunk as any;
resolvedChunk.status = RESOLVED_MODEL;
resolvedChunk.value = value;
resolvedChunk.reason = response;
if (resolveListeners !== null) {
// This is unfortunate that we're reading this eagerly if
// we already have listeners attached since they might no
// longer be rendered or might not be the highest pri.
// 如果我们已经附加了监听器,那么我们急切地读取这一点是很不幸的,
// 因为它们可能不再被渲染,或者可能不是最高优先级。
initializeModelChunk(resolvedChunk);
// The status might have changed after initialization.
// 状态可能在初始化后发生变化。
wakeChunkIfInitialized(response, chunk, resolveListeners, rejectListeners);
}
}
解析模块块
function resolveModuleChunk<T>(
response: Response,
chunk: SomeChunk<T>,
value: ClientReference<T>,
): void {
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
// We already resolved. We didn't expect to see this.
// 我们已经解决了。我们没想到会看到这个。
return;
}
releasePendingChunk(response, chunk);
const resolveListeners = chunk.value;
const rejectListeners = chunk.reason;
const resolvedChunk: ResolvedModuleChunk<T> = chunk as any;
resolvedChunk.status = RESOLVED_MODULE;
resolvedChunk.value = value;
resolvedChunk.reason = null;
if (__DEV__) {
const debugInfo = getModuleDebugInfo(value);
if (debugInfo !== null) {
// Add to the live set if it was already initialized.
// 如果已经初始化,则添加到活动集合中。
resolvedChunk._debugInfo.push.apply(resolvedChunk._debugInfo, debugInfo);
}
}
if (resolveListeners !== null) {
initializeModuleChunk(resolvedChunk);
wakeChunkIfInitialized(response, chunk, resolveListeners, rejectListeners);
}
}
初始化调试块
function initializeDebugChunk(
response: Response,
chunk: ResolvedModelChunk<any> | PendingChunk<any>,
): void {
const debugChunk = chunk._debugChunk;
if (debugChunk !== null) {
const debugInfo = chunk._debugInfo;
const prevIsInitializingDebugInfo = isInitializingDebugInfo;
isInitializingDebugInfo = true;
try {
if (debugChunk.status === RESOLVED_MODEL) {
// Find the index of this debug info by walking the linked list.
// 通过遍历链表查找此调试信息的索引。
let idx = debugInfo.length;
let c = debugChunk._debugChunk;
while (c !== null) {
if (c.status !== INITIALIZED) {
idx++;
}
c = c._debugChunk;
}
// Initializing the model for the first time.
// 第一次初始化模型。
initializeModelChunk(debugChunk);
const initializedChunk = debugChunk as any as SomeChunk<any>;
switch (initializedChunk.status) {
case INITIALIZED: {
debugInfo[idx] = initializeDebugInfo(
response,
initializedChunk.value,
);
break;
}
case BLOCKED:
case PENDING: {
waitForReference(
initializedChunk,
debugInfo,
'' + idx,
response,
initializeDebugInfo,
// 路径
[''], // path
true,
);
break;
}
default:
throw initializedChunk.reason;
}
} else {
switch (debugChunk.status) {
case INITIALIZED: {
// Already done.
// 已完成。
break;
}
case BLOCKED:
case PENDING: {
// Signal to the caller that we need to wait.
// 向调用者发出我们需要等待的信号。
waitForReference(
debugChunk,
// 无操作,因为我们已经向调试信息添加了一个条目
{}, // noop, since we'll have already added an entry to debug info
// 无操作,但我们需要它不是空字符串,因为那表示根对象
'debug', // noop, but we need it to not be empty string since that indicates the root object
response,
initializeDebugInfo,
// 路径
[''], // path
true,
);
break;
}
default:
throw debugChunk.reason;
}
}
} catch (error) {
triggerErrorOnChunk(response, chunk, error);
} finally {
isInitializingDebugInfo = prevIsInitializingDebugInfo;
}
}
}
初始化模型块
function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
const prevHandler = initializingHandler;
const prevChunk = initializingChunk;
initializingHandler = null;
const resolvedModel = chunk.value;
const response = chunk.reason;
// We go to the BLOCKED state until we've fully resolved this.
// 我们会进入 BLOCKED 状态,直到我们完全解决这个问题。
// We do this before parsing in case we try to initialize the same chunk
// while parsing the model. Such as in a cyclic reference.
// 我们在解析之前这样做,以防我们在解析模型时尝试初始化同一块。例如在循环引用中。
const cyclicChunk: BlockedChunk<T> = chunk as any;
cyclicChunk.status = BLOCKED;
cyclicChunk.value = null;
cyclicChunk.reason = null;
if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {
initializingChunk = cyclicChunk;
}
if (__DEV__) {
// Initialize any debug info and block the initializing chunk on any
// unresolved entries.
// 初始化任何调试信息,并在存在任何未解析条目时阻塞初始化块。
initializeDebugChunk(response, chunk);
// TODO: The chunk might have transitioned to ERRORED now.
// Should we return early if that happens?
// TODO: 这个块现在可能已经转为 ERRORED 状态。
// 如果发生这种情况,我们是否应该提前返回?
}
try {
const value: T = parseModel(response, resolvedModel);
// Invoke any listeners added while resolving this model. I.e. cyclic
// references. This may or may not fully resolve the model depending on
// if they were blocked.
// 调用在解析此模型时添加的任何监听器。即循环
// 引用。根据它们是否被阻止,这可能会完全解析模型,也可能不会。
const resolveListeners = cyclicChunk.value;
if (resolveListeners !== null) {
cyclicChunk.value = null;
cyclicChunk.reason = null;
for (let i = 0; i < resolveListeners.length; i++) {
const listener = resolveListeners[i];
if (typeof listener === 'function') {
listener(value);
} else {
fulfillReference(response, listener, value, cyclicChunk);
}
}
}
if (initializingHandler !== null) {
if (initializingHandler.errored) {
throw initializingHandler.reason;
}
if (initializingHandler.deps > 0) {
// We discovered new dependencies on modules that are not yet resolved.
// We have to keep the BLOCKED state until they're resolved.
// 我们发现了尚未解决的模块新依赖。
// 在这些依赖解决之前,我们必须保持 BLOCKED 状态。
initializingHandler.value = value;
initializingHandler.chunk = cyclicChunk;
return;
}
}
const initializedChunk: InitializedChunk<T> = chunk as any;
initializedChunk.status = INITIALIZED;
initializedChunk.value = value;
initializedChunk.reason = null;
if (__DEV__) {
processChunkDebugInfo(response, initializedChunk, value);
}
} catch (error) {
const erroredChunk: ErroredChunk<T> = chunk as any;
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
} finally {
initializingHandler = prevHandler;
if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {
initializingChunk = prevChunk;
}
}
}
初始化模块块
function initializeModuleChunk<T>(chunk: ResolvedModuleChunk<T>): void {
try {
const value: T = requireModule(chunk.value);
const initializedChunk: InitializedChunk<T> = chunk as any;
initializedChunk.status = INITIALIZED;
initializedChunk.value = value;
initializedChunk.reason = null;
} catch (error) {
const erroredChunk: ErroredChunk<T> = chunk as0 any;
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
}
}
空引用获取器
function nullRefGetter() {
if (__DEV__) {
return null;
}
}
获取 IO 信息任务名称
function getIOInfoTaskName(ioInfo: ReactIOInfo): string {
return ioInfo.name || 'unknown';
}
获取异步信息任务名称
function getAsyncInfoTaskName(asyncInfo: ReactAsyncInfo): string {
return 'await ' + getIOInfoTaskName(asyncInfo.awaited);
}
获取服务器组件任务名称
function getServerComponentTaskName(componentInfo: ReactComponentInfo): string {
return '<' + (componentInfo.name || '...') + '>';
}
获取任务名称
function getTaskName(type: mixed): string {
if (type === REACT_FRAGMENT_TYPE) {
return '<>';
}
if (typeof type === 'function') {
// This is a function so it must have been a Client Reference that resolved to
// a function. We use "use client" to indicate that this is the boundary into
// the client. There should only be one for any given owner chain.
// This is a function so it must have been a Client Reference that resolved to
// a function. We use "use client" to indicate that this is the boundary into
// the client. There should only be one for any given owner chain.
return '"use client"';
}
if (
typeof type === 'object' &&
type !== null &&
type.$$typeof === REACT_LAZY_TYPE
) {
if (type._init === readChunk) {
// This is a lazy node created by Flight. It is probably a client reference.
// We use the "use client" string to indicate that this is the boundary into
// the client. There will only be one for any given owner chain.
// 这是由 Flight 创建的懒节点。它可能是客户端引用。
// 我们使用 "use client" 字符串来表示这是进入客户端的边界。
// 对于任何给定的所有者链,只有一个这样的节点。
return '"use client"';
}
// We don't want to eagerly initialize the initializer in DEV mode so we can't
// call it to extract the type so we don't know the type of this component.
// 我们不想在开发模式下急切地初始化初始化器,所以我们不能调用它来提取类型,因此我们
// 不知道这个组件的类型。
return '<...>';
}
try {
const name = getComponentNameFromType(type);
return name ? '<' + name + '>' : '<...>';
} catch (x) {
return '<...>';
}
}
初始化元素
function initializeElement(
response: Response,
element: any,
lazyNode: null | LazyComponent<
React$Element<any>,
SomeChunk<React$Element<any>>
>,
): void {
if (!__DEV__) {
return;
}
const stack = element._debugStack;
const owner = element._owner;
if (owner === null) {
element._owner = response._debugRootOwner;
}
let env = response._rootEnvironmentName;
if (owner !== null && owner.env != null) {
// Interestingly we don't actually have the environment name of where
// this JSX was created if it doesn't have an owner but if it does
// it must be the same environment as the owner. We could send it separately
// but it seems a bit unnecessary for this edge case.
// 有趣的是,如果这个 JSX 没有所有者,我们实际上并不知道它创建的环境名称,但如果有所有者,它
// 必须与所有者的环境相同。我们可以单独发送它,但对于这种边缘情况似乎有点不必要。
env = owner.env;
}
let normalizedStackTrace: null | Error = null;
if (owner === null && response._debugRootStack != null) {
// We override the stack if we override the owner since the stack where the root JSX
// was created on the server isn't very useful but where the request was made is.
// 如果我们重写所有者,我们就会重写堆栈,因为在服务器上创建根 JSX 的堆栈不是很有用,但请求发
// 生的位置却很有用。
normalizedStackTrace = response._debugRootStack;
} else if (stack !== null) {
// We create a fake stack and then create an Error object inside of it.
// 我们创建一个假堆栈,然后在其中创建一个 Error 对象。
// This means that the stack trace is now normalized into the native format
// of the browser and the stack frames will have been registered with
// source mapping information.
// 这意味着堆栈跟踪现在已被规范化为浏览器的原生格式,堆栈帧将已经注册有源映射信息。
// This can unfortunately happen within a user space callstack which will
// remain on the stack.
// 不幸的是,这可能会发生在用户空间的调用栈中,并且将保持在堆栈上。
normalizedStackTrace = createFakeJSXCallStackInDEV(response, stack, env);
}
element._debugStack = normalizedStackTrace;
let task: null | ConsoleTask = null;
if (supportsCreateTask && stack !== null) {
const createTaskFn = (console as any).createTask.bind(
console,
getTaskName(element.type),
);
const callStack = buildFakeCallStack(
response,
stack,
env,
false,
createTaskFn,
);
// This owner should ideally have already been initialized to avoid getting
// user stack frames on the stack.
// 理想情况下,这个所有者应该已经被初始化,以避免在堆栈上获取用户堆栈帧。
const ownerTask =
owner === null ? null : initializeFakeTask(response, owner);
if (ownerTask === null) {
const rootTask = response._debugRootTask;
if (rootTask != null) {
task = rootTask.run(callStack);
} else {
task = callStack();
}
} else {
task = ownerTask.run(callStack);
}
}
element._debugTask = task;
// This owner should ideally have already been initialized to avoid getting
// user stack frames on the stack.
// 理想情况下,这个所有者应该已经被初始化,以避免在堆栈上获取用户堆栈帧。
if (owner !== null) {
initializeFakeStack(response, owner);
}
if (lazyNode !== null) {
// In case the JSX runtime has validated the lazy type as a static child, we
// need to transfer this information to the element.
// 如果 JSX 运行时已经将 lazy 类型验证为静态子元素,
// 我们需要将此信息传递给该元素。
if (
lazyNode._store &&
lazyNode._store.validated &&
!element._store.validated
) {
element._store.validated = lazyNode._store.validated;
}
// If the lazy node is initialized, we move its debug info to the inner
// value.
// 如果懒节点已初始化,我们将其调试信息移动到内部值。
if (lazyNode._payload.status === INITIALIZED && lazyNode._debugInfo) {
const debugInfo = lazyNode._debugInfo.splice(0);
if (element._debugInfo) {
element._debugInfo.unshift.apply(element._debugInfo, debugInfo);
} else {
Object.defineProperty(element, '_debugInfo', {
configurable: false,
enumerable: false,
writable: true,
value: debugInfo,
});
}
}
}
// TODO: We should be freezing the element but currently, we might write into
// _debugInfo later. We could move it into _store which remains mutable.
// 待办事项:我们应该冻结该元素,但目前我们可能会在以后写入 _debugInfo。我们可以把它移到仍然
// 可变的 _store 中。
Object.freeze(element.props);
}
创建元素
function createElement(
response: Response,
type: mixed,
key: mixed,
props: mixed,
owner: ?ReactComponentInfo, // DEV-only
stack: ?ReactStackTrace, // DEV-only
validated: 0 | 1 | 2, // DEV-only
):
| React$Element<any>
| LazyComponent<React$Element<any>, SomeChunk<React$Element<any>>> {
let element: any;
if (__DEV__) {
// `ref` is non-enumerable in dev
// 在开发环境中,`ref` 是不可枚举的
element = {
$$typeof: REACT_ELEMENT_TYPE,
type,
key,
props,
_owner: owner === undefined ? null : owner,
} as any;
Object.defineProperty(element, 'ref', {
enumerable: false,
get: nullRefGetter,
});
} else {
element = {
// This tag allows us to uniquely identify this as a React Element
// 这个标签允许我们将其唯一地识别为一个 React 元素
$$typeof: REACT_ELEMENT_TYPE,
type,
key,
ref: null,
props,
} as any;
}
if (__DEV__) {
// We don't really need to add any of these but keeping them for good measure.
// 我们实际上不需要添加这些,但为了保险起见还是保留它们。
// Unfortunately, _store is enumerable in jest matchers so for equality to
// work, I need to keep it or make _store non-enumerable in the other file.
// 不幸的是,_store 在 jest 匹配器中是可枚举的,所以为了使相等性生效,我需要保留它,或者在
// 另一个文件中将 _store 设置为不可枚举。
element._store = {} as {
validated?: number;
};
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
// 元素是否已经在服务器上被验证。
value: validated, // Whether the element has already been validated on the server.
});
// debugInfo contains Server Component debug information.
// debugInfo 包含服务器组件的调试信息。
Object.defineProperty(element, '_debugInfo', {
configurable: false,
enumerable: false,
writable: true,
value: null,
});
Object.defineProperty(element, '_debugStack', {
configurable: false,
enumerable: false,
writable: true,
value: stack === undefined ? null : stack,
});
Object.defineProperty(element, '_debugTask', {
configurable: false,
enumerable: false,
writable: true,
value: null,
});
}
if (initializingHandler !== null) {
const handler = initializingHandler;
// We pop the stack to the previous outer handler before leaving the Element.
// This is effectively the complete phase.
// 在离开元素之前,我们将堆栈回退到前一个外部处理器。这实际上是完整的阶段。
initializingHandler = handler.parent;
if (handler.errored) {
// Something errored inside this Element's props. We can turn this Element
// into a Lazy so that we can still render up until that Lazy is rendered.
// 这个元素的 props 内部出现了错误。我们可以将这个元素转换为 Lazy,这样我们仍然可以在该
// Lazy 被渲染之前渲染它。
const erroredChunk: ErroredChunk<React$Element<any>> = createErrorChunk(
response,
handler.reason,
);
if (__DEV__) {
initializeElement(response, element, null);
// Conceptually the error happened inside this Element but right before
// it was rendered. We don't have a client side component to render but
// we can add some DebugInfo to explain that this was conceptually a
// Server side error that errored inside this element. That way any stack
// traces will point to the nearest JSX that errored - e.g. during
// serialization.
// 从概念上讲,错误发生在这个元素内部,但就在它被渲染之前。我们没有客户端组件来渲染,但
// 我们可以添加一些调试信息来解释这是一个概念上的服务器端错误,发生在这个元素内部。这
// 样,任何堆栈跟踪都会指向最近发生错误的 JSX,例如在序列化期间。
const erroredComponent: ReactComponentInfo = {
name: getComponentNameFromType(element.type) || '',
owner: element._owner,
};
erroredComponent.debugStack = element._debugStack;
if (supportsCreateTask) {
erroredComponent.debugTask = element._debugTask;
}
erroredChunk._debugInfo = [erroredComponent];
}
return createLazyChunkWrapper(erroredChunk, validated);
}
if (handler.deps > 0) {
// We have blocked references inside this Element but we can turn this into
// a Lazy node referencing this Element to let everything around it proceed.
// 我们已阻止此元素内部的引用,但我们可以将其转换为一个延迟节点引用此元素,以让其周围的一
// 切继续进行。
const blockedChunk: BlockedChunk<React$Element<any>> =
createBlockedChunk(response);
handler.value = element;
handler.chunk = blockedChunk;
const lazyNode = createLazyChunkWrapper(blockedChunk, validated);
if (__DEV__) {
// After we have initialized any blocked references, initialize stack etc.
// 在我们初始化了任何被阻塞的引用之后,初始化堆栈等。
const init = initializeElement.bind(null, response, element, lazyNode);
blockedChunk.then(init, init);
}
return lazyNode;
}
}
if (__DEV__) {
initializeElement(response, element, null);
}
return element;
}
创建懒加载模块包装器
function createLazyChunkWrapper<T>(
chunk: SomeChunk<T>,
validated: 0 | 1 | 2, // DEV-only
): LazyComponent<T, SomeChunk<T>> {
const lazyType: LazyComponent<T, SomeChunk<T>> = {
$$typeof: REACT_LAZY_TYPE,
_payload: chunk,
_init: readChunk,
};
if (__DEV__) {
// Forward the live array
// 转发实时数组
lazyType._debugInfo = chunk._debugInfo;
// Initialize a store for key validation by the JSX runtime.
// 为 JSX 运行时初始化一个用于键验证的存储。
lazyType._store = { validated: validated };
}
return lazyType;
}
获取块
function getChunk(response: Response, id: number): SomeChunk<any> {
const chunks = response._chunks;
let chunk = chunks.get(id);
if (!chunk) {
if (response._closed) {
if (response._allowPartialStream) {
// For partial streams, chunks accessed after close should be HALTED
// (never resolve).
// 对于部分流,在关闭后访问的块应被停止(永远不应解析)。
chunk = createPendingChunk(response);
const haltedChunk: HaltedChunk<any> = chunk as any;
haltedChunk.status = HALTED;
haltedChunk.value = null;
haltedChunk.reason = null;
} else {
// We have already errored the response and we're not going to get
// anything more streaming in so this will immediately error.
// 我们已经使响应出错,并且不会有更多流式数据传入,因此这将立即出错。
chunk = createErrorChunk(response, response._closedReason);
}
} else {
chunk = createPendingChunk(response);
}
chunks.set(id, chunk);
}
return chunk;
}
履行参考
function fulfillReference(
response: Response,
reference: InitializationReference,
value: any,
fulfilledChunk: SomeChunk<any>,
): void {
const { handler, parentObject, key, map, path } = reference;
try {
for (let i = 1; i < path.length; i++) {
while (
typeof value === 'object' &&
value !== null &&
value.$$typeof === REACT_LAZY_TYPE
) {
// We never expect to see a Lazy node on this path because we encode those as
// separate models. This must mean that we have inserted an extra lazy node
// e.g. to replace a blocked element. We must instead look for it inside.
// 我们从不期望在这条路径上看到 Lazy 节点,因为我们将它们编码为单独的模型。这一定意味着
// 我们插入了一个额外的 Lazy 节点,例如,用来替换被阻塞的元素。我们必须改为在内部查找它。
const referencedChunk: SomeChunk<any> = value._payload;
if (referencedChunk === handler.chunk) {
// This is a reference to the thing we're currently blocking. We can peak
// inside of it to get the value.
// 这是对我们当前阻塞的事物的引用。我们可以窥探它来获取值。
value = handler.value;
continue;
} else {
switch (referencedChunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(referencedChunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(referencedChunk);
break;
}
switch (referencedChunk.status) {
case INITIALIZED: {
value = referencedChunk.value;
continue;
}
case BLOCKED: {
// It is possible that we're blocked on our own chunk if it's a cycle.
// 如果它是一个循环,我们可能会被自己的块阻塞。
// Before adding the listener to the inner chunk, let's check if it would
// result in a cycle.
// 在将监听器添加到内部块之前,让我们先检查是否会导致循环。
const cyclicHandler = resolveBlockedCycle(
referencedChunk,
reference,
);
if (cyclicHandler !== null) {
// This reference points back to this chunk. We can resolve the cycle by
// using the value from that handler.
// 这个引用指回这个块。我们可以通过使用该处理程序的值来解决循环。
value = cyclicHandler.value;
continue;
}
// Fallthrough
// 穿透
}
case PENDING: {
// If we're not yet initialized we need to skip what we've already drilled
// through and then wait for the next value to become available.
// 如果我们还没有初始化,我们需要跳过已经处理过的内容,然后等待下一个值可用。
path.splice(0, i - 1);
// Add "listener" to our new chunk dependency.
// 将 “listener” 添加到我们的新块依赖中。
if (referencedChunk.value === null) {
referencedChunk.value = [reference];
} else {
referencedChunk.value.push(reference);
}
if (referencedChunk.reason === null) {
referencedChunk.reason = [reference];
} else {
referencedChunk.reason.push(reference);
}
return;
}
case HALTED: {
// Do nothing. We couldn't fulfill.
// 什么也不做。我们无法完成。
// TODO: Mark downstreams as halted too.
// TODO:也将下游标记为已停止。
return;
}
default: {
rejectReference(
response,
reference.handler,
referencedChunk.reason,
);
return;
}
}
}
}
const name = path[i];
if (
typeof value === 'object' &&
value !== null &&
hasOwnProperty.call(value, name)
) {
value = value[name];
} else {
throw new Error('Invalid reference.');
}
}
while (
typeof value === 'object' &&
value !== null &&
value.$$typeof === REACT_LAZY_TYPE
) {
// If what we're referencing is a Lazy it must be because we inserted one as a virtual node
// while it was blocked by other data. If it's no longer blocked, we can unwrap it.
// 如果我们所引用的是一个惰性节点,那一定是因为我们在它被其他数据阻塞时将其作为虚拟节点插
// 入。如果它不再被阻塞,我们就可以将其展开。
const referencedChunk: SomeChunk<any> = value._payload;
if (referencedChunk === handler.chunk) {
// This is a reference to the thing we're currently blocking. We can peak
// inside of it to get the value.
// 这是对我们当前阻塞的事物的引用。我们可以窥探它来获取值。
value = handler.value;
continue;
} else {
switch (referencedChunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(referencedChunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(referencedChunk);
break;
}
switch (referencedChunk.status) {
case INITIALIZED: {
value = referencedChunk.value;
continue;
}
}
}
break;
}
const mappedValue = map(response, value, parentObject, key);
if (key !== __PROTO__) {
parentObject[key] = mappedValue;
}
// If this is the root object for a model reference, where `handler.value`
// is a stale `null`, the resolved value can be used directly.
// 如果这是模型引用的根对象,其中 `handler.value` 是过时的 `null`,则可以直接使用解析后的值。
if (key === '' && handler.value === null) {
handler.value = mappedValue;
}
// If the parent object is an unparsed React element tuple, we also need to
// update the props and owner of the parsed element object (i.e.
// handler.value).
// 如果父对象是未解析的 React 元素元组,我们也需要更新已解析元素对象(即 handler.value)
// 的 props 和所有者。
if (
parentObject[0] === REACT_ELEMENT_TYPE &&
typeof handler.value === 'object' &&
handler.value !== null &&
handler.value.$$typeof === REACT_ELEMENT_TYPE
) {
const element: any = handler.value;
switch (key) {
case '3':
if (__DEV__) {
transferReferencedDebugInfo(handler.chunk, fulfilledChunk);
}
element.props = mappedValue;
break;
case '4':
// This path doesn't call transferReferencedDebugInfo because this reference is to a debug chunk.
// 这条路径不会调用 transferReferencedDebugInfo,因为这个引用指向一个调试块。
if (__DEV__) {
element._owner = mappedValue;
}
break;
case '5':
// This path doesn't call transferReferencedDebugInfo because this reference is to a debug chunk.
// 这条路径不会调用 transferReferencedDebugInfo,因为这个引用指向一个调试块。
if (__DEV__) {
element._debugStack = mappedValue;
}
break;
default:
if (__DEV__) {
transferReferencedDebugInfo(handler.chunk, fulfilledChunk);
}
break;
}
} else if (__DEV__ && !reference.isDebug) {
transferReferencedDebugInfo(handler.chunk, fulfilledChunk);
}
} catch (error) {
rejectReference(response, reference.handler, error);
return;
}
handler.deps--;
if (handler.deps === 0) {
const chunk = handler.chunk;
if (chunk === null || chunk.status !== BLOCKED) {
return;
}
const resolveListeners = chunk.value;
const initializedChunk: InitializedChunk<any> = chunk as any;
initializedChunk.status = INITIALIZED;
initializedChunk.value = handler.value;
// 用于流式传输块
initializedChunk.reason = handler.reason; // Used by streaming chunks
if (resolveListeners !== null) {
wakeChunk(response, resolveListeners, handler.value, initializedChunk);
} else {
if (__DEV__) {
processChunkDebugInfo(response, initializedChunk, handler.value);
}
}
}
}
拒绝引用
function rejectReference(
response: Response,
handler: InitializationHandler,
error: mixed,
): void {
if (handler.errored) {
// We've already errored. We could instead build up an AggregateError
// but if there are multiple errors we just take the first one like
// Promise.all.
// 我们已经出错了。我们可以改为构建一个 AggregateError 但是如果有多个错误,我们就像
// Promise.all 一样只取第一个。
return;
}
const blockedValue = handler.value;
handler.errored = true;
handler.value = null;
handler.reason = error;
const chunk = handler.chunk;
if (chunk === null || chunk.status !== BLOCKED) {
return;
}
if (__DEV__) {
if (
typeof blockedValue === 'object' &&
blockedValue !== null &&
blockedValue.$$typeof === REACT_ELEMENT_TYPE
) {
const element = blockedValue;
// Conceptually the error happened inside this Element but right before
// it was rendered. We don't have a client side component to render but
// we can add some DebugInfo to explain that this was conceptually a
// Server side error that errored inside this element. That way any stack
// traces will point to the nearest JSX that errored - e.g. during
// serialization.
// 从概念上讲,错误发生在这个元素内部,但就在它被渲染之前。我们没有客户端组件来渲染,但我
// 们可以添加一些调试信息来解释这是一个概念上的服务器端错误,发生在这个元素内部。这样,任
// 何堆栈跟踪都会指向最近发生错误的 JSX,例如在序列化期间。
const erroredComponent: ReactComponentInfo = {
name: getComponentNameFromType(element.type) || '',
owner: element._owner,
};
erroredComponent.debugStack = element._debugStack;
if (supportsCreateTask) {
erroredComponent.debugTask = element._debugTask;
}
chunk._debugInfo.push(erroredComponent);
}
}
triggerErrorOnChunk(response, chunk, error);
}
等待引用
function waitForReference<T>(
referencedChunk: PendingChunk<T> | BlockedChunk<T>,
parentObject: Object,
key: string,
response: Response,
map: (response: Response, model: any, parentObject: Object, key: string) => T,
path: Array<string>,
isAwaitingDebugInfo: boolean, // DEV-only
): T {
if (
__DEV__ &&
(response._debugChannel === undefined ||
!response._debugChannel.hasReadable)
) {
if (
referencedChunk.status === PENDING &&
parentObject[0] === REACT_ELEMENT_TYPE &&
(key === '4' || key === '5')
) {
// If the parent object is an unparsed React element tuple, and this is a reference
// to the owner or debug stack. Then we expect the chunk to have been emitted earlier
// in the stream. It might be blocked on other things but chunk should no longer be pending.
//
// 如果父对象是未解析的 React 元素元组,并且这是对所有者或调试堆栈的引用。那么我们希望这
// 个数据块已经在流的早期阶段被发出。它可能被其他东西阻塞,但数据块不应再处于挂起状态。
//
// If it's still pending that suggests that it was referencing an object in the debug
// channel, but no debug channel was wired up so it's missing. In this case we can just
// drop the debug info instead of halting the whole stream.
// 如果它仍然挂起,这表明它引用了调试通道中的对象,但没有连接调试通道,所以缺失了。
// 在这种情况下,我们可以只丢弃调试信息,而不是停止整个流。
return null as any;
}
}
let handler: InitializationHandler;
if (initializingHandler) {
handler = initializingHandler;
handler.deps++;
} else {
handler = initializingHandler = {
parent: null,
chunk: null,
value: null,
reason: null,
deps: 1,
errored: false,
};
}
const reference: InitializationReference = {
handler,
parentObject,
key,
map,
path,
};
if (__DEV__) {
reference.isDebug = isAwaitingDebugInfo;
}
// Add "listener".
// 添加“监听器”。
if (referencedChunk.value === null) {
referencedChunk.value = [reference];
} else {
referencedChunk.value.push(reference);
}
if (referencedChunk.reason === null) {
referencedChunk.reason = [reference];
} else {
referencedChunk.reason.push(reference);
}
// Return a place holder value for now.
// 目前返回一个占位值。
return null as any;
}
加载服务器引用
function loadServerReference<A extends Iterable<any>, T>(
response: Response,
metaData: {
id: any;
bound: null | Thenable<Array<any>>;
name?: string; // DEV-only
env?: string; // DEV-only
location?: ReactFunctionLocation; // DEV-only
},
parentObject: Object,
key: string,
// ): (...A) => Promise<T> {
): (...a: A) => Promise<T> {
if (!response._serverReferenceConfig) {
// In the normal case, we can't load this Server Reference in the current environment and
// we just return a proxy to it.
// 在正常情况下,我们无法在当前环境中加载此服务器引用,我们只是返回一个代理对象。
return createBoundServerReference(
metaData,
response._callServer,
response._encodeFormAction,
__DEV__ ? response._debugFindSourceMapURL : undefined,
);
}
// If we have a module mapping we can load the real version of this Server Reference.
// 如果我们有模块映射,我们可以加载此服务器引用的真实版本。
const serverReference: ClientReference<T> =
resolveServerReference<$FlowFixMe>(
response._serverReferenceConfig,
metaData.id,
);
let promise: null | Thenable<any> = preloadModule(serverReference);
if (!promise) {
if (!metaData.bound) {
const resolvedValue = requireModule(serverReference) as any;
registerBoundServerReference(
resolvedValue,
metaData.id,
metaData.bound,
response._encodeFormAction,
);
return resolvedValue;
} else {
promise = Promise.resolve(metaData.bound);
}
} else if (metaData.bound) {
promise = Promise.all([promise, metaData.bound]);
}
let handler: InitializationHandler;
if (initializingHandler) {
handler = initializingHandler;
handler.deps++;
} else {
handler = initializingHandler = {
parent: null,
chunk: null,
value: null,
reason: null,
deps: 1,
errored: false,
};
}
function fulfill(): void {
let resolvedValue = requireModule(serverReference) as any;
if (metaData.bound) {
// This promise is coming from us and should have initilialized by now.
// 这个 Promise 是由我们发起的,现在应该已经被初始化了。
const boundArgs: Array<any> = (metaData.bound as any).value.slice(0);
boundArgs.unshift(null); // this
resolvedValue = resolvedValue.bind.apply(resolvedValue, boundArgs);
}
registerBoundServerReference(
resolvedValue,
metaData.id,
metaData.bound,
response._encodeFormAction,
);
if (key !== __PROTO__) {
parentObject[key] = resolvedValue;
}
// If this is the root object for a model reference, where `handler.value`
// is a stale `null`, the resolved value can be used directly.
// 如果这是模型引用的根对象,其中 `handler.value` 是过时的 `null`,则可以直接使用解析后的值。
if (key === '' && handler.value === null) {
handler.value = resolvedValue;
}
// If the parent object is an unparsed React element tuple, we also need to
// update the props and owner of the parsed element object (i.e.
// handler.value).
// 如果父对象是未解析的 React 元素元组,我们也需要更新解析后元素对象(即 handler.value)
// 的 props 和所有者。
if (
parentObject[0] === REACT_ELEMENT_TYPE &&
typeof handler.value === 'object' &&
handler.value !== null &&
handler.value.$$typeof === REACT_ELEMENT_TYPE
) {
const element: any = handler.value;
switch (key) {
case '3':
element.props = resolvedValue;
break;
case '4':
if (__DEV__) {
element._owner = resolvedValue;
}
break;
}
}
handler.deps--;
if (handler.deps === 0) {
const chunk = handler.chunk;
if (chunk === null || chunk.status !== BLOCKED) {
return;
}
const resolveListeners = chunk.value;
const initializedChunk: InitializedChunk<T> = chunk as any;
initializedChunk.status = INITIALIZED;
initializedChunk.value = handler.value;
initializedChunk.reason = null;
if (resolveListeners !== null) {
wakeChunk(response, resolveListeners, handler.value, initializedChunk);
} else {
if (__DEV__) {
processChunkDebugInfo(response, initializedChunk, handler.value);
}
}
}
}
function reject(error: mixed): void {
if (handler.errored) {
// We've already errored. We could instead build up an AggregateError
// but if there are multiple errors we just take the first one like
// Promise.all.
// 我们已经出错了。我们可以改为构建一个 AggregateError 。但是如果有多个错误,我们就像 Promise.all 一样只取第一个。
return;
}
const blockedValue = handler.value;
handler.errored = true;
handler.value = null;
handler.reason = error;
const chunk = handler.chunk;
if (chunk === null || chunk.status !== BLOCKED) {
return;
}
if (__DEV__) {
if (
typeof blockedValue === 'object' &&
blockedValue !== null &&
blockedValue.$$typeof === REACT_ELEMENT_TYPE
) {
const element = blockedValue;
// Conceptually the error happened inside this Element but right before
// it was rendered. We don't have a client side component to render but
// we can add some DebugInfo to explain that this was conceptually a
// Server side error that errored inside this element. That way any stack
// traces will point to the nearest JSX that errored - e.g. during
// serialization.
// 从概念上讲,错误发生在这个元素内部,但就在它被渲染之前。
// 我们没有客户端组件来渲染,但我们可以添加一些调试信息来解释这是一个概念上的服务器端错误,发生在这个元素内部。
// 这样,任何堆栈跟踪都会指向最近发生错误的 JSX,例如在序列化期间。
const erroredComponent: ReactComponentInfo = {
name: getComponentNameFromType(element.type) || '',
owner: element._owner,
};
erroredComponent.debugStack = element._debugStack;
if (supportsCreateTask) {
erroredComponent.debugTask = element._debugTask;
}
chunk._debugInfo.push(erroredComponent);
}
}
triggerErrorOnChunk(response, chunk, error);
}
promise.then(fulfill, reject);
// Return a place holder value for now.
// 目前返回一个占位值。
return null as any;
}
解决延迟
function resolveLazy(value: any): mixed {
while (
typeof value === 'object' &&
value !== null &&
value.$$typeof === REACT_LAZY_TYPE
) {
const payload: SomeChunk<any> = value._payload;
if (payload.status === INITIALIZED) {
value = payload.value;
continue;
}
break;
}
return value;
}
传输引用的调试信息
function transferReferencedDebugInfo(
parentChunk: null | SomeChunk<any>,
referencedChunk: SomeChunk<any>,
): void {
if (__DEV__) {
// We add the debug info to the initializing chunk since the resolution of
// that promise is also blocked by the referenced debug info. By adding it
// to both we can track it even if the array/element/lazy is extracted, or
// if the root is rendered as is.
// 我们将调试信息添加到初始化块中,因为该 Promise 的解析也会被引用的调试信息阻塞。通过将其
// 添加到两者中,即使数组/元素/懒加载被提取,或者根节点按原样呈现,我们也可以跟踪它。
if (parentChunk !== null) {
const referencedDebugInfo = referencedChunk._debugInfo;
const parentDebugInfo = parentChunk._debugInfo;
for (let i = 0; i < referencedDebugInfo.length; ++i) {
const debugInfoEntry = referencedDebugInfo[i];
if (debugInfoEntry.name != null) {
debugInfoEntry as ReactComponentInfo;
// We're not transferring Component info since we use Component info
// in Debug info to fill in gaps between Fibers for the parent stack.
// 我们没有传输组件信息,因为我们在调试信息中使用组件信息来填补父堆栈中 Fiber 之间的空白。
} else {
parentDebugInfo.push(debugInfoEntry);
}
}
}
}
}
获取轮廓模型
function getOutlinedModel<T>(
response: Response,
reference: string,
parentObject: Object,
key: string,
map: (response: Response, model: any, parentObject: Object, key: string) => T,
): T {
const path = reference.split(':');
const id = parseInt(path[0], 16);
const chunk = getChunk(response, id);
if (enableProfilerTimer && enableComponentPerformanceTrack) {
if (initializingChunk !== null && isArray(initializingChunk._children)) {
initializingChunk._children.push(chunk);
}
}
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(chunk);
break;
}
// The status might have changed after initialization.
// 状态可能在初始化后发生变化。
switch (chunk.status) {
case INITIALIZED:
let value = chunk.value;
for (let i = 1; i < path.length; i++) {
while (
typeof value === 'object' &&
value !== null &&
value.$$typeof === REACT_LAZY_TYPE
) {
const referencedChunk: SomeChunk<any> = value._payload;
switch (referencedChunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(referencedChunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(referencedChunk);
break;
}
switch (referencedChunk.status) {
case INITIALIZED: {
value = referencedChunk.value;
break;
}
case BLOCKED:
case PENDING: {
return waitForReference(
referencedChunk,
parentObject,
key,
response,
map,
path.slice(i - 1),
isInitializingDebugInfo,
);
}
case HALTED: {
// Add a dependency that will never resolve.
// 添加一个永远无法解决的依赖。
// TODO: Mark downstreams as halted too.
// TODO:也将下游标记为停止。
let handler: InitializationHandler;
if (initializingHandler) {
handler = initializingHandler;
handler.deps++;
} else {
handler = initializingHandler = {
parent: null,
chunk: null,
value: null,
reason: null,
deps: 1,
errored: false,
};
}
return null as any;
}
default: {
// This is an error. Instead of erroring directly, we're going to encode this on
// an initialization handler so that we can catch it at the nearest Element.
// 这是一个错误。我们不会直接报错,而是将在初始化处理程序上对其进行编码,以便我们
// 可以在最近的元素处捕获它。
if (initializingHandler) {
initializingHandler.errored = true;
initializingHandler.value = null;
initializingHandler.reason = referencedChunk.reason;
} else {
initializingHandler = {
parent: null,
chunk: null,
value: null,
reason: referencedChunk.reason,
deps: 0,
errored: true,
};
}
return null as any;
}
}
}
value = value[path[i]];
}
while (
typeof value === 'object' &&
value !== null &&
value.$$typeof === REACT_LAZY_TYPE
) {
// If what we're referencing is a Lazy it must be because we inserted one as a virtual node
// while it was blocked by other data. If it's no longer blocked, we can unwrap it.
// 如果我们所引用的是一个惰性节点,那一定是因为我们在它被其他数据阻塞时将其作为虚拟节点
// 插入。// 如果它不再被阻塞,我们就可以将其展开。
const referencedChunk: SomeChunk<any> = value._payload;
switch (referencedChunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(referencedChunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(referencedChunk);
break;
}
switch (referencedChunk.status) {
case INITIALIZED: {
value = referencedChunk.value;
continue;
}
}
break;
}
const chunkValue = map(response, value, parentObject, key);
if (__DEV__) {
if (
parentObject[0] === REACT_ELEMENT_TYPE &&
(key === '4' || key === '5')
) {
// If we're resolving the "owner" or "stack" slot of an Element array,
// we don't call transferReferencedDebugInfo because this reference is
// to a debug chunk.
// 如果我们正在解析一个元素数组的“owner”或“stack”槽,我们不会调用
// transferReferencedDebugInfo,因为此引用是指向调试块的。
} else if (isInitializingDebugInfo) {
// If we're resolving references as part of debug info resolution, we
// don't call transferReferencedDebugInfo because these references are
// to debug chunks.
// 如果我们在解析调试信息时解析引用,我们不会调用 transferReferencedDebugInfo,
// 因为这些引用是指向调试块的。
} else {
transferReferencedDebugInfo(initializingChunk, chunk);
}
}
return chunkValue;
case PENDING:
case BLOCKED:
return waitForReference(
chunk,
parentObject,
key,
response,
map,
path,
isInitializingDebugInfo,
);
case HALTED: {
// Add a dependency that will never resolve.
// 添加一个永远无法解决的依赖。
// TODO: Mark downstreams as halted too.
// TODO: 也将下游标记为已停止。
let handler: InitializationHandler;
if (initializingHandler) {
handler = initializingHandler;
handler.deps++;
} else {
handler = initializingHandler = {
parent: null,
chunk: null,
value: null,
reason: null,
deps: 1,
errored: false,
};
}
return null as any;
}
default:
// This is an error. Instead of erroring directly, we're going to encode this on
// an initialization handler so that we can catch it at the nearest Element.
// 这是一个错误。我们不会直接报错,而是将在初始化处理程序上对其进行编码,以便我们可以在最
// 近的元素处捕获它。
if (initializingHandler) {
initializingHandler.errored = true;
initializingHandler.value = null;
initializingHandler.reason = chunk.reason;
} else {
initializingHandler = {
parent: null,
chunk: null,
value: null,
reason: chunk.reason,
deps: 0,
errored: true,
};
}
// Placeholder
// 占位符
return null as any;
}
}
创建 Map
function createMap(
response: Response,
model: Array<[any, any]>,
): Map<any, any> {
return new Map(model);
}
构建 Set
function createSet(response: Response, model: Array<any>): Set<any> {
return new Set(model);
}
构建 Blob
function createBlob(response: Response, model: Array<any>): Blob {
return new Blob(model.slice(1), { type: model[0] });
}
构建表单数据
function createFormData(
response: Response,
model: Array<[any, any]>,
): FormData {
const formData = new FormData();
for (let i = 0; i < model.length; i++) {
formData.append(model[i][0], model[i][1]);
}
return formData;
}
应用构造函数
function applyConstructor(
response: Response,
model: Function,
parentObject: Object,
key: string,
): void {
Object.setPrototypeOf(parentObject, model.prototype);
// Delete the property. It was just a placeholder.
return undefined;
}
定义惰性获取器
function defineLazyGetter<T>(
response: Response,
chunk: SomeChunk<T>,
parentObject: Object,
key: string,
): any {
// We don't immediately initialize it even if it's resolved.
// 即使它已被解析,我们也不会立即初始化它。
// Instead, we wait for the getter to get accessed.
// 相反,我们会等待访问 getter 时才初始化。
if (key !== __PROTO__) {
Object.defineProperty(parentObject, key, {
get: function () {
if (chunk.status === RESOLVED_MODEL) {
// If it was now resolved, then we initialize it. This may then discover
// a new set of lazy references that are then asked for eagerly in case
// we get that deep.
// 如果现在已解决,那么我们初始化它。这可能会发现一组新的延迟引用,然后在可能深入的情
// 况下被急切地请求。
initializeModelChunk(chunk);
}
switch (chunk.status) {
case INITIALIZED: {
return chunk.value;
}
case ERRORED:
throw chunk.reason;
}
// Otherwise, we didn't have enough time to load the object before it was
// accessed or the connection closed. So we just log that it was omitted.
// 否则,我们没有足够的时间在对象被访问或连接关闭之前加载它。所以我们只是记录它被省略了。
// TODO: We should ideally throw here to indicate a difference.
// TODO: 理想情况下我们应该在这里抛出异常以指示差异。
return OMITTED_PROP_ERROR;
},
// no-op: the walk function may try to reassign this property after
// parseModelString returns. With the JSON.parse reviver, the engine's
// internal CreateDataProperty silently failed. We use a no-op setter
// to match that behavior in strict mode.
// 无操作:walk 函数可能会在 parseModelString 返回后尝试重新分配此属性。
// 使用 JSON.parse 的重建器时,引擎的内部 CreateDataProperty 会静默失败。
// 我们使用无操作的 setter 来在严格模式下匹配这种行为。
set: function () {},
enumerable: true,
configurable: false,
});
}
return null;
}
提取迭代器
function extractIterator(response: Response, model: Array<any>): Iterator<any> {
return model[Symbol.iterator]();
}
创建模型
function createModel(response: Response, model: any): any {
return model;
}
获取推断函数近似
function getInferredFunctionApproximate(code: string): () => void {
let slicedCode;
if (code.startsWith('Object.defineProperty(')) {
slicedCode = code.slice('Object.defineProperty('.length);
} else if (code.startsWith('(')) {
slicedCode = code.slice(1);
} else {
slicedCode = code;
}
if (slicedCode.startsWith('async function')) {
const idx = slicedCode.indexOf('(', 14);
if (idx !== -1) {
const name = slicedCode.slice(14, idx).trim();
return (0, eval)('({' + JSON.stringify(name) + ':async function(){}})')[
name
];
}
} else if (slicedCode.startsWith('function')) {
const idx = slicedCode.indexOf('(', 8);
if (idx !== -1) {
const name = slicedCode.slice(8, idx).trim();
return (0, eval)('({' + JSON.stringify(name) + ':function(){}})')[name];
}
} else if (slicedCode.startsWith('class')) {
const idx = slicedCode.indexOf('{', 5);
if (idx !== -1) {
const name = slicedCode.slice(5, idx).trim();
return (0, eval)('({' + JSON.stringify(name) + ':class{}})')[name];
}
}
return function () {};
}
解析模型字符串
function parseModelString(
response: Response,
parentObject: Object,
key: string,
value: string,
): any {
if (value[0] === '$') {
if (value === '$') {
// A very common symbol.
if (initializingHandler !== null && key === '0') {
// We we already have an initializing handler and we're abound to enter
// a new element, we need to shadow it because we're now in a new scope.
// This is effectively the "begin" or "push" phase of Element parsing.
// We'll pop later when we parse the array itself.
// 我们已经有一个初始化处理程序,并且我们即将进入一个新元素,我们需要对其进行遮蔽,因为
// 我们现在处于一个新作用域。这实际上是元素解析的“开始”或“压入”阶段。当我们解析数组本身
// 时,我们稍后会将其弹出。
initializingHandler = {
parent: initializingHandler,
chunk: null,
value: null,
reason: null,
deps: 0,
errored: false,
};
}
return REACT_ELEMENT_TYPE;
}
switch (value[1]) {
case '$': {
// This was an escaped string value.
// 这是一个转义的字符串值。
return value.slice(1);
}
case 'L': {
// Lazy node
// 懒惰节点
const id = parseInt(value.slice(2), 16);
const chunk = getChunk(response, id);
if (enableProfilerTimer && enableComponentPerformanceTrack) {
if (
initializingChunk !== null &&
isArray(initializingChunk._children)
) {
initializingChunk._children.push(chunk);
}
}
// We create a React.lazy wrapper around any lazy values.
// 我们为任何惰性值创建一个 React.lazy 包装器。
// When passed into React, we'll know how to suspend on this.
// 当传入 React 时,我们就会知道如何在此处挂起。
return createLazyChunkWrapper(chunk, 0);
}
case '@': {
// Promise
// 承诺
const id = parseInt(value.slice(2), 16);
const chunk = getChunk(response, id);
if (enableProfilerTimer && enableComponentPerformanceTrack) {
if (
initializingChunk !== null &&
isArray(initializingChunk._children)
) {
initializingChunk._children.push(chunk);
}
}
return chunk;
}
case 'S': {
// Symbol
// Symbol
return Symbol.for(value.slice(2));
}
case 'h': {
// Server Reference
// 服务器参考
const ref = value.slice(2);
return getOutlinedModel(
response,
ref,
parentObject,
key,
loadServerReference,
);
}
case 'T': {
// Temporary Reference
// 临时参考
const reference = '$' + value.slice(2);
const temporaryReferences = response._tempRefs;
if (temporaryReferences == null) {
throw new Error(
'Missing a temporary reference set but the RSC response returned a temporary reference. ' +
'Pass a temporaryReference option with the set that was used with the reply.',
);
}
return readTemporaryReference(temporaryReferences, reference);
}
case 'Q': {
// Map
// Map
const ref = value.slice(2);
return getOutlinedModel(response, ref, parentObject, key, createMap);
}
case 'W': {
// Set
// Set
const ref = value.slice(2);
return getOutlinedModel(response, ref, parentObject, key, createSet);
}
case 'B': {
// Blob
// Blob
const ref = value.slice(2);
return getOutlinedModel(response, ref, parentObject, key, createBlob);
}
case 'K': {
// FormData
// 表单数据
const ref = value.slice(2);
return getOutlinedModel(
response,
ref,
parentObject,
key,
createFormData,
);
}
case 'Z': {
// Error
// 错误
if (__DEV__) {
const ref = value.slice(2);
return getOutlinedModel(
response,
ref,
parentObject,
key,
resolveErrorDev,
);
} else {
return resolveErrorProd(response);
}
}
case 'i': {
// Iterator
// 迭代器
const ref = value.slice(2);
return getOutlinedModel(
response,
ref,
parentObject,
key,
extractIterator,
);
}
case 'I': {
// $Infinity
// $无限
return Infinity;
}
case '-': {
// $-0 or $-Infinity
// $-0 或 $-Infinity
if (value === '$-0') {
return -0;
} else {
return -Infinity;
}
}
case 'N': {
// $NaN
// NaN
return NaN;
}
case 'u': {
// matches "$undefined"
// 匹配 "$undefined"
// Special encoding for `undefined` which can't be serialized as JSON otherwise.
// `undefined` 的特殊编码,否则无法作为 JSON 序列化。
return undefined;
}
case 'D': {
// Date
// 日期
return new Date(Date.parse(value.slice(2)));
}
case 'n': {
// BigInt
// 大整数
return BigInt(value.slice(2));
}
case 'P': {
if (__DEV__) {
// In DEV mode we allow debug objects to specify themselves as instances of
// another constructor.
// 在开发模式下,我们允许调试对象将自己指定为另一个构造函数的实例。
const ref = value.slice(2);
return getOutlinedModel(
response,
ref,
parentObject,
key,
applyConstructor,
);
}
// Fallthrough
// 贯穿
}
case 'E': {
if (__DEV__) {
// In DEV mode we allow indirect eval to produce functions for logging.
// This should not compile to eval() because then it has local scope access.
// 在开发模式下,我们允许间接 eval 来生成用于日志记录的函数。这不应该编译为 eval
// (),因为那样它将具有本地作用域的访问权限。
const code = value.slice(2);
try {
// If this might be a class constructor with a static initializer or
// static constructor then don't eval it. It might cause unexpected
// side-effects. Instead, fallback to parsing out the function type
// and name.
// 如果这可能是带有静态初始化器或静态构造函数的类构造函数,则不要评估它。它可能会导
// 致意外的副作用。相反,退回到解析函数类型和名称。
if (!mightHaveStaticConstructor.test(code)) {
return (0, eval)(code);
}
} catch (x) {
// Fallthrough to fallback case.
// 继续执行到备用情况。
}
// We currently use this to express functions so we fail parsing it,
// let's just return a blank function as a place holder.
// 我们目前用这个来表示函数,所以解析失败,我们就返回一个空函数作为占位符。
let fn;
try {
fn = getInferredFunctionApproximate(code);
if (code.startsWith('Object.defineProperty(')) {
const DESCRIPTOR = ',"name",{value:"';
const idx = code.lastIndexOf(DESCRIPTOR);
if (idx !== -1) {
const name = JSON.parse(
code.slice(idx + DESCRIPTOR.length - 1, code.length - 2),
);
Object.defineProperty(fn, 'name', { value: name });
}
}
} catch (_) {
fn = function () {};
}
return fn;
}
// Fallthrough
// 穿透
}
case 'Y': {
if (__DEV__) {
if (value.length > 2) {
const debugChannelCallback =
response._debugChannel && response._debugChannel.callback;
if (debugChannelCallback) {
if (value[2] === '@') {
// This is a deferred Promise.
// 这是一个延迟的 Promise。
// 我们假设这只有 id,没有路径。
const ref = value.slice(3); // We assume this doesn't have a path just id.
const id = parseInt(ref, 16);
if (!response._chunks.has(id)) {
// We haven't seen this id before. Query the server to start sending it.
// 我们以前没有见过这个 ID。查询服务器以开始发送它。
debugChannelCallback('P:' + ref);
}
// Start waiting. This now creates a pending chunk if it doesn't already exist.
// This is the actual Promise we're waiting for.
// 开始等待。如果尚不存在,这现在会创建一个挂起的块。这是我们实际等待的 Promise。
return getChunk(response, id);
}
// 我们假设这只有 id,而没有路径。
const ref = value.slice(2); // We assume this doesn't have a path just id.
const id = parseInt(ref, 16);
if (!response._chunks.has(id)) {
// We haven't seen this id before. Query the server to start sending it.
// 我们以前没有见过这个 ID。查询服务器以开始发送它。
debugChannelCallback('Q:' + ref);
}
// Start waiting. This now creates a pending chunk if it doesn't already exist.
// 开始等待。如果尚不存在,这将创建一个待处理的块。
const chunk = getChunk(response, id);
if (chunk.status === INITIALIZED) {
// We already loaded this before. We can just use the real value.
// 我们之前已经加载过这个了。我们可以直接使用真实值。
return chunk.value;
}
return defineLazyGetter(response, chunk, parentObject, key);
}
}
// In DEV mode we encode omitted objects in logs as a getter that throws
// so that when you try to access it on the client, you know why that
// happened.
// 在开发模式下,我们将日志中省略的对象编码为一个抛出异常的 getter
// 这样当你在客户端尝试访问它时,你就会知道为什么会发生这种情况。
if (key !== __PROTO__) {
Object.defineProperty(parentObject, key, {
get: function () {
// TODO: We should ideally throw here to indicate a difference.
// 待办:理想情况下我们应该在这里抛出以指示差异。
return OMITTED_PROP_ERROR;
},
// no-op: the walk function may try to reassign this property
// after parseModelString returns. With the JSON.parse reviver,
// the engine's internal CreateDataProperty silently failed.
// 无操作:walk 函数可能会尝试在 parseModelString 返回后重新分配此属性
// 使用 JSON.parse 的 reviver 时,
// 引擎的内部 CreateDataProperty 会静默失败。
// We use a no-op setter to match that behavior in strict mode.
// 我们使用无操作的 setter 来在严格模式下匹配该行为。
set: function () {},
enumerable: true,
configurable: false,
});
}
return null;
}
// Fallthrough
// 穿透
}
default: {
// We assume that anything else is a reference ID.
// 我们假设其他任何东西都是参考ID。
const ref = value.slice(1);
return getOutlinedModel(response, ref, parentObject, key, createModel);
}
}
}
return value;
}
解析模型元组
function parseModelTuple(
response: Response,
// value: {+[key: string]: JSONValue} | $ReadOnlyArray<JSONValue>,
value: { [key: string]: JSONValue } | $ReadOnlyArray<JSONValue>,
): any {
const tuple: [mixed, mixed, mixed, mixed] = value as any;
if (tuple[0] === REACT_ELEMENT_TYPE) {
// TODO: Consider having React just directly accept these arrays as elements.
// Or even change the ReactElement type to be an array.
// 待办事项:考虑让 React 直接将这些数组作为元素接受。
// 或者甚至将 ReactElement 类型更改为数组。
return createElement(
response,
tuple[1],
tuple[2],
tuple[3],
__DEV__ ? (tuple as any)[4] : null,
__DEV__ ? (tuple as any)[5] : null,
__DEV__ ? (tuple as any)[6] : 0,
);
}
return value;
}
未接来电
function missingCall() {
throw new Error(
'Trying to call a function from "use server" but the callServer option ' +
'was not implemented in your router runtime.',
);
}
标记 IO 已启动
function markIOStarted(this: Response) {
this._debugIOStarted = true;
}
响应实例
function ResponseInstance(
this: $FlowFixMe,
bundlerConfig: ServerConsumerModuleMap,
serverReferenceConfig: null | ServerManifest,
moduleLoading: ModuleLoading,
callServer: void | CallServerCallback,
encodeFormAction: void | EncodeFormActionCallback,
nonce: void | string,
temporaryReferences: void | TemporaryReferenceSet,
allowPartialStream: boolean,
findSourceMapURL: void | FindSourceMapURLCallback, // DEV-only
replayConsole: boolean, // DEV-only
environmentName: void | string, // DEV-only
debugStartTime: void | number, // DEV-only
debugEndTime: void | number, // DEV-only
debugChannel: void | DebugChannel, // DEV-only
) {
const chunks: Map<number, SomeChunk<any>> = new Map();
this._bundlerConfig = bundlerConfig;
this._serverReferenceConfig = serverReferenceConfig;
this._moduleLoading = moduleLoading;
this._callServer = callServer !== undefined ? callServer : missingCall;
this._encodeFormAction = encodeFormAction;
this._nonce = nonce;
this._chunks = chunks;
this._stringDecoder = createStringDecoder();
this._closed = false;
this._closedReason = null;
this._allowPartialStream = allowPartialStream;
this._tempRefs = temporaryReferences;
if (enableProfilerTimer && enableComponentPerformanceTrack) {
this._timeOrigin = 0;
this._pendingInitialRender = null;
}
if (__DEV__) {
this._pendingChunks = 0;
this._weakResponse = {
weak: new WeakRef(this),
response: this,
};
// TODO: The Flight Client can be used in a Client Environment too and we should really support
// getting the owner there as well, but currently the owner of ReactComponentInfo is typed as only
// supporting other ReactComponentInfo as owners (and not Fiber or Fizz's ComponentStackNode).
// 待办事项:Flight 客户端也可以在客户端环境中使用,我们确实应该支持在那里获取所有者,但目
// 前 ReactComponentInfo 的所有者类型仅支持其他 ReactComponentInfo 作为所有者(而不
// 是 Fiber 或 Fizz 的 ComponentStackNode)。
// We need to update all the callsites consuming ReactComponentInfo owners to support those.
// In the meantime we only check ReactSharedInteralsServer since we know that in an RSC environment
// the only owners will be ReactComponentInfo.
// 我们需要更新所有使用 ReactComponentInfo 所有者的调用点以支持这些类型。与此同时,我们只
// 检查 ReactSharedInternalsServer,因为我们知道在 RSC 环境中唯一的所有者将是
// ReactComponentInfo。
const rootOwner: null | ReactComponentInfo =
ReactSharedInteralsServer === undefined ||
ReactSharedInteralsServer.A === null
? null
: (ReactSharedInteralsServer.A.getOwner() as any);
this._debugRootOwner = rootOwner;
this._debugRootStack =
rootOwner !== null
? // TODO: Consider passing the top frame in so we can avoid internals showing up.
// 待办:考虑传入顶层框架,这样我们可以避免内部内容显示出来。
new Error('react-stack-top-frame')
: null;
const rootEnv = environmentName === undefined ? 'Server' : environmentName;
if (supportsCreateTask) {
// Any stacks that appear on the server need to be rooted somehow on the client
// so we create a root Task for this response which will be the root owner for any
// elements created by the server. We use the "use server" string to indicate that
// this is where we enter the server from the client.
// 服务器上出现的任何堆栈都需要以某种方式在客户端被固定。因此我们为这个响应创建一个根
// Task,它将成为服务器创建的任何元素的根所有者。我们使用 "use server" 字符串来表示这是
// 从客户端进入服务器的地方。
// TODO: Make this string configurable.
// TODO: 使这个字符串可配置。
this._debugRootTask = (console as any).createTask(
'"use ' + rootEnv.toLowerCase() + '"',
);
}
if (enableAsyncDebugInfo) {
// Track the start of the fetch to the best of our knowledge.
// Note: createFromFetch allows this to be marked at the start of the fetch
// where as if you use createFromReadableStream from the body of the fetch
// then the start time is when the headers resolved.
// 尽我们所知,跟踪 fetch 的开始。
// 注意:createFromFetch 允许在 fetch 开始时进行标记
// 而如果你使用 fetch 的 body 中的 createFromReadableStream
// 那么开始时间是头信息解析完成的时刻。
this._debugStartTime =
debugStartTime == null ? performance.now() : debugStartTime;
this._debugIOStarted = false;
// We consider everything before the first setTimeout task to be cached data
// and is not considered I/O required to load the stream.
// 我们将第一个 setTimeout 任务之前的所有内容视为缓存数据
// 并且不认为这是加载流所需的 I/O。
setTimeout(markIOStarted.bind(this), 0);
}
this._debugEndTime = debugEndTime == null ? null : debugEndTime;
this._debugFindSourceMapURL = findSourceMapURL;
this._debugChannel = debugChannel;
this._blockedConsole = null;
this._replayConsole = replayConsole;
this._rootEnvironmentName = rootEnv;
if (debugChannel) {
if (debugChannelRegistry === null) {
// We can't safely clean things up later, so we immediately close the
// debug channel.
// 我们无法在之后安全地清理,因此我们会立即关闭调试通道。
closeDebugChannel(debugChannel);
this._debugChannel = undefined;
} else {
// When a Response gets GC:ed because nobody is referring to any of the
// objects that lazily load from the Response anymore, then we can close
// the debug channel.
// 由于我们不知道配置文件的录制何时开始和停止,我们不得不一遍又一遍地标记顺序。 ...
debugChannelRegistry.register(this, debugChannel, this);
}
}
}
if (enableProfilerTimer && enableComponentPerformanceTrack) {
// Since we don't know when recording of profiles will start and stop, we have to
// mark the order over and over again.
// 由于我们不知道配置文件的录制何时开始和停止,我们不得不一遍又一遍地标记顺序。
if (replayConsole) {
markAllTracksInOrder();
}
}
}
增加块调试信息
function incrementChunkDebugInfo(
streamState: StreamState,
chunkLength: number,
): void {
if (__DEV__ && enableAsyncDebugInfo) {
const debugInfo: ReactIOInfo = streamState._debugInfo;
const endTime = performance.now();
const previousEndTime = debugInfo.end;
const newByteLength = (debugInfo.byteSize as any as number) + chunkLength;
if (
newByteLength > streamState._debugTargetChunkSize ||
endTime > previousEndTime + 10
) {
// This new chunk would overshoot the chunk size so therefore we treat it as its own new chunk
// by cloning the old one. Similarly, if some time has passed we assume that it was actually
// due to the server being unable to flush chunks faster e.g. due to I/O so it would be a
// new chunk in production even if the buffer hasn't been reached.
// 这个新的数据块会超过块大小,因此我们将其视为一个新的数据块通过克隆旧的数据块来实现。类
// 似地,如果经过了一段时间,我们假设这实际上是由于服务器无法更快地刷新数据块,例如由于
// I/O 问题,因此即使缓冲区未达到上限,在生产环境中它也会是一个新的数据块。
streamState._debugInfo = {
name: debugInfo.name,
start: debugInfo.start,
end: endTime,
byteSize: newByteLength,
value: debugInfo.value,
owner: debugInfo.owner,
debugStack: debugInfo.debugStack,
debugTask: debugInfo.debugTask,
};
streamState._debugTargetChunkSize = newByteLength + MIN_CHUNK_SIZE;
} else {
// Otherwise we reuse the old chunk but update the end time and byteSize to the latest.
// 否则我们重用旧的块,但将结束时间和字节大小更新为最新值。
debugInfo.end = endTime;
debugInfo.byteSize = newByteLength;
}
}
}
添加异步信息
function addAsyncInfo(chunk: SomeChunk<any>, asyncInfo: ReactAsyncInfo): void {
const value = resolveLazy(chunk.value);
if (
typeof value === 'object' &&
value !== null &&
(isArray(value) ||
typeof value[ASYNC_ITERATOR] === 'function' ||
value.$$typeof === REACT_ELEMENT_TYPE ||
value.$$typeof === REACT_LAZY_TYPE)
) {
if (isArray(value._debugInfo)) {
value._debugInfo.push(asyncInfo);
} else if (!Object.isFrozen(value)) {
// TODO: Debug info is dropped for frozen elements. See the TODO in
// moveDebugInfoFromChunkToInnerValue.
// TODO:调试信息已被丢弃用于冻结元素。请参阅 moveDebugInfoFromChunkToInnerValue
// 中的 TODO。
Object.defineProperty(value as any, '_debugInfo', {
configurable: false,
enumerable: false,
writable: true,
value: [asyncInfo],
});
}
} else {
chunk._debugInfo.push(asyncInfo);
}
}
解析块调试信息
function resolveChunkDebugInfo(
response: Response,
streamState: StreamState,
chunk: SomeChunk<any>,
): void {
if (__DEV__ && enableAsyncDebugInfo) {
// Only include stream information after a macrotask. Any chunk processed
// before that is considered cached data.
// 仅在宏任务之后包含流信息。在此之前处理的任何块都被视为缓存数据。
if (response._debugIOStarted) {
// Add the currently resolving chunk's debug info representing the stream
// to the Promise that was waiting on the stream, or its underlying value.
// 将当前正在解析的块的调试信息(表示流)
// 添加到正在等待该流的 Promise,或其底层值。
const asyncInfo: ReactAsyncInfo = { awaited: streamState._debugInfo };
if (chunk.status === PENDING || chunk.status === BLOCKED) {
const boundAddAsyncInfo = addAsyncInfo.bind(null, chunk, asyncInfo);
chunk.then(boundAddAsyncInfo, boundAddAsyncInfo);
} else {
addAsyncInfo(chunk, asyncInfo);
}
}
}
}
解决调试暂停
function resolveDebugHalt(response: Response, id: number): void {
const chunks = response._chunks;
let chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, (chunk = createPendingChunk(response)));
} else {
}
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
return;
}
releasePendingChunk(response, chunk);
const haltedChunk: HaltedChunk<any> = chunk as any;
haltedChunk.status = HALTED;
haltedChunk.value = null;
haltedChunk.reason = null;
}
解析模型
function resolveModel(
response: Response,
id: number,
model: UninitializedModel,
streamState: StreamState,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
const newChunk: ResolvedModelChunk<any> = createResolvedModelChunk(
response,
model,
);
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, newChunk);
}
chunks.set(id, newChunk);
} else {
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, chunk);
}
resolveModelChunk(response, chunk, model);
}
}
解析文本
function resolveText(
response: Response,
id: number,
text: string,
streamState: StreamState,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
if (chunk && chunk.status !== PENDING) {
// If we get more data to an already resolved ID, we assume that it's
// a stream chunk since any other row shouldn't have more than one entry.
// 如果我们对一个已解析的 ID 获取更多数据,我们假设这是一个流块,因为任何其他行不应该有多于
// 一个的条目。
const streamChunk: InitializedStreamChunk<any> = chunk as any;
const controller = streamChunk.reason;
controller.enqueueValue(text);
return;
}
if (chunk) {
releasePendingChunk(response, chunk);
}
const newChunk = createInitializedTextChunk(response, text);
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, newChunk);
}
chunks.set(id, newChunk);
}
解析缓冲区
function resolveBuffer(
response: Response,
id: number,
buffer: $ArrayBufferView | ArrayBuffer,
streamState: StreamState,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
if (chunk && chunk.status !== PENDING) {
// If we get more data to an already resolved ID, we assume that it's
// a stream chunk since any other row shouldn't have more than one entry.
// 如果我们获取到已解析 ID 的更多数据,我们假设它是一个流块,因为任何其他行不应该有超过一个
// 条目。
const streamChunk: InitializedStreamChunk<any> = chunk as any;
const controller = streamChunk.reason;
controller.enqueueValue(buffer);
return;
}
if (chunk) {
releasePendingChunk(response, chunk);
}
const newChunk = createInitializedBufferChunk(response, buffer);
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, newChunk);
}
chunks.set(id, newChunk);
}
解析模块
function resolveModule(
response: Response,
id: number,
model: UninitializedModel,
streamState: StreamState,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
const clientReferenceMetadata: ClientReferenceMetadata = parseModel(
response,
model,
);
const clientReference = resolveClientReference<$FlowFixMe>(
response._bundlerConfig,
clientReferenceMetadata,
);
prepareDestinationForModule(
response._moduleLoading,
response._nonce,
clientReferenceMetadata,
);
// TODO: Add an option to encode modules that are lazy loaded.
// For now we preload all modules as early as possible since it's likely
// that we'll need them.
// 待办事项:添加一个选项来编码惰性加载的模块。
// 目前我们尽可能早地预加载所有模块,因为很可能我们会需要它们。
const promise = preloadModule(clientReference);
if (promise) {
let blockedChunk: BlockedChunk<any>;
if (!chunk) {
// Technically, we should just treat promise as the chunk in this
// case. Because it'll just behave as any other promise.
// 从技术上讲,在这种情况下我们应该把 promise 当作块来处理。
// 因为它的行为就像其他任何 promise 一样。
blockedChunk = createBlockedChunk(response);
chunks.set(id, blockedChunk);
} else {
releasePendingChunk(response, chunk);
// This can't actually happen because we don't have any forward
// references to modules.
// 这实际上不可能发生,因为我们没有对模块的任何前向引用。
blockedChunk = chunk as any;
blockedChunk.status = BLOCKED;
}
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, blockedChunk);
}
promise.then(
() => resolveModuleChunk(response, blockedChunk, clientReference),
error => triggerErrorOnChunk(response, blockedChunk, error),
);
} else {
if (!chunk) {
const newChunk = createResolvedModuleChunk(response, clientReference);
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, newChunk);
}
chunks.set(id, newChunk);
} else {
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, chunk);
}
// This can't actually happen because we don't have any forward
// references to modules.
// 这实际上不可能发生,因为我们没有对模块的任何前向引用。
resolveModuleChunk(response, chunk, clientReference);
}
}
}
解析流
function resolveStream<
T extends ReadableStream | $AsyncIterable<any, any, void>,
>(
response: Response,
id: number,
stream: T,
controller: FlightStreamController,
streamState: StreamState,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
const newChunk = createInitializedStreamChunk(response, stream, controller);
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, newChunk);
}
chunks.set(id, newChunk);
return;
}
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, chunk);
}
if (chunk.status !== PENDING) {
// We already resolved. We didn't expect to see this.
// 我们已经解决了。我们没想到会看到这个。
return;
}
const resolveListeners = chunk.value;
if (__DEV__) {
// Initialize any debug info and block the initializing chunk on any
// unresolved entries.
// 初始化任何调试信息,并在存在任何未解析条目时阻塞初始化块。
if (chunk._debugChunk != null) {
const prevHandler = initializingHandler;
const prevChunk = initializingChunk;
initializingHandler = null;
const cyclicChunk: BlockedChunk<T> = chunk as any;
cyclicChunk.status = BLOCKED;
cyclicChunk.value = null;
cyclicChunk.reason = null;
if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {
initializingChunk = cyclicChunk;
}
try {
initializeDebugChunk(response, chunk);
if (initializingHandler !== null) {
if (initializingHandler.errored) {
// Ignore error parsing debug info, we'll report the original error instead.
// 忽略解析调试信息时的错误,我们将改为报告原始错误。
} else if (initializingHandler.deps > 0) {
// Leave blocked until we can resolve all the debug info.
// 保持阻塞,直到我们能够解决所有调试信息。
initializingHandler.value = stream;
initializingHandler.reason = controller;
initializingHandler.chunk = cyclicChunk;
return;
}
}
} finally {
initializingHandler = prevHandler;
initializingChunk = prevChunk;
}
}
}
const resolvedChunk: InitializedStreamChunk<T> = chunk as any;
resolvedChunk.status = INITIALIZED;
resolvedChunk.value = stream;
resolvedChunk.reason = controller;
if (resolveListeners !== null) {
wakeChunk(response, resolveListeners, chunk.value, chunk as any);
} else {
if (__DEV__) {
processChunkDebugInfo(response, resolvedChunk, stream);
}
}
}
启动可读流
function startReadableStream<T>(
response: Response,
id: number,
type: void | 'bytes',
streamState: StreamState,
): void {
let controller: ReadableStreamController = null as any;
let closed = false;
const stream = new ReadableStream({
type: type,
start(c) {
controller = c;
},
});
let previousBlockedChunk: SomeChunk<T> | null = null;
const flightController = {
enqueueValue(value: T): void {
if (previousBlockedChunk === null) {
controller.enqueue(value);
} else {
// We're still waiting on a previous chunk so we can't enqueue quite yet.
// 我们仍在等待之前的一个块,所以我们还不能排队。
previousBlockedChunk.then(function () {
controller.enqueue(value);
});
}
},
enqueueModel(json: UninitializedModel): void {
if (previousBlockedChunk === null) {
// If we're not blocked on any other chunks, we can try to eagerly initialize
// this as a fast-path to avoid awaiting them.
// 如果我们在其他任何块上没有被阻塞,我们可以尝试急切地初始化这是一个快速路径,以避免等
// 待它们。
const chunk: ResolvedModelChunk<T> = createResolvedModelChunk(
response,
json,
);
initializeModelChunk(chunk);
const initializedChunk: SomeChunk<T> = chunk;
if (initializedChunk.status === INITIALIZED) {
controller.enqueue(initializedChunk.value);
} else {
chunk.then(
v => controller.enqueue(v),
e => controller.error(e as any),
);
previousBlockedChunk = chunk;
}
} else {
// We're still waiting on a previous chunk so we can't enqueue quite yet.
// 我们仍在等待之前的一个块,所以我们还不能排队。
const blockedChunk = previousBlockedChunk;
const chunk: SomeChunk<T> = createPendingChunk(response);
chunk.then(
v => controller.enqueue(v),
e => controller.error(e as any),
);
previousBlockedChunk = chunk;
blockedChunk.then(function () {
if (previousBlockedChunk === chunk) {
// We were still the last chunk so we can now clear the queue and return
// to synchronous emitting.
// 我们仍然是最后一块,所以现在可以清空队列并返回到同步发射模式。
previousBlockedChunk = null;
}
resolveModelChunk(response, chunk, json);
});
}
},
close(json: UninitializedModel): void {
if (closed) {
return;
}
closed = true;
if (previousBlockedChunk === null) {
controller.close();
} else {
const blockedChunk = previousBlockedChunk;
// We shouldn't get any more enqueues after this so we can set it back to null.
// 在此之后我们不应该有更多的入队操作,所以我们可以将其设置回 null。
previousBlockedChunk = null;
blockedChunk.then(() => controller.close());
}
},
error(error: mixed): void {
if (closed) {
return;
}
closed = true;
if (previousBlockedChunk === null) {
controller.error(error);
} else {
const blockedChunk = previousBlockedChunk;
// We shouldn't get any more enqueues after this so we can set it back to null.
// 在此之后我们不应该有更多的入队操作,所以我们可以将其设置回 null。
previousBlockedChunk = null;
blockedChunk.then(() => controller.error(error as any));
}
},
};
resolveStream(response, id, stream, flightController, streamState);
}
异步迭代器
function asyncIterator(this: $AsyncIterator<any, any, void>) {
// Self referencing iterator.
// 自我引用的迭代器。
return this;
}
创建迭代器
function createIterator<T>(
next: (arg: void) => SomeChunk<IteratorResult<T, T>>,
): $AsyncIterator<T, T, void> {
const iterator: any = {
next: next,
// TODO: Add return/throw as options for aborting.
// 待办事项:添加 return/throw 作为中止的选项。
};
// TODO: The iterator could inherit the AsyncIterator prototype which is not exposed as
// a global but exists as a prototype of an AsyncGenerator. However, it's not needed
// to satisfy the iterable protocol.
// TODO:迭代器可以继承 AsyncIterator 原型,该原型不是全局暴露的,但作为 AsyncGenerator
// 的原型存在。然而,为了满足可迭代协议,这不是必须的。
(iterator as any)[ASYNC_ITERATOR] = asyncIterator;
return iterator;
}
启动异步可迭代对象
function startAsyncIterable<T>(
response: Response,
id: number,
iterator: boolean,
streamState: StreamState,
): void {
const buffer: Array<SomeChunk<IteratorResult<T, T>>> = [];
let closed = false;
let nextWriteIndex = 0;
const flightController = {
enqueueValue(value: T): void {
if (nextWriteIndex === buffer.length) {
buffer[nextWriteIndex] = createInitializedIteratorResultChunk(
response,
value,
false,
);
} else {
const chunk: PendingChunk<IteratorResult<T, T>> = buffer[
nextWriteIndex
] as any;
const resolveListeners = chunk.value;
const rejectListeners = chunk.reason;
const initializedChunk: InitializedChunk<IteratorResult<T, T>> =
chunk as any;
initializedChunk.status = INITIALIZED;
initializedChunk.value = { done: false, value: value };
initializedChunk.reason = null;
if (resolveListeners !== null) {
wakeChunkIfInitialized(
response,
chunk,
resolveListeners,
rejectListeners,
);
}
}
nextWriteIndex++;
},
enqueueModel(value: UninitializedModel): void {
if (nextWriteIndex === buffer.length) {
buffer[nextWriteIndex] = createResolvedIteratorResultChunk(
response,
value,
false,
);
} else {
resolveIteratorResultChunk(
response,
buffer[nextWriteIndex],
value,
false,
);
}
nextWriteIndex++;
},
close(value: UninitializedModel): void {
if (closed) {
return;
}
closed = true;
if (nextWriteIndex === buffer.length) {
buffer[nextWriteIndex] = createResolvedIteratorResultChunk(
response,
value,
true,
);
} else {
resolveIteratorResultChunk(
response,
buffer[nextWriteIndex],
value,
true,
);
}
nextWriteIndex++;
while (nextWriteIndex < buffer.length) {
// In generators, any extra reads from the iterator have the value undefined.
// 在生成器中,从迭代器的任何额外读取的值都是 undefined。
resolveIteratorResultChunk(
response,
buffer[nextWriteIndex++],
'"$undefined"',
true,
);
}
},
error(error: Error): void {
if (closed) {
return;
}
closed = true;
if (nextWriteIndex === buffer.length) {
buffer[nextWriteIndex] =
createPendingChunk<IteratorResult<T, T>>(response);
}
while (nextWriteIndex < buffer.length) {
triggerErrorOnChunk(response, buffer[nextWriteIndex++], error);
}
},
};
const iterable: $AsyncIterable<T, T, void> = {} as any;
iterable[ASYNC_ITERATOR] = (): $AsyncIterator<T, T, void> => {
let nextReadIndex = 0;
return createIterator(arg => {
if (arg !== undefined) {
throw new Error(
'Values cannot be passed to next() of AsyncIterables passed to Client Components.',
);
}
if (nextReadIndex === buffer.length) {
if (closed) {
return new ReactPromise(
INITIALIZED,
{ done: true, value: undefined },
null,
);
}
buffer[nextReadIndex] =
createPendingChunk<IteratorResult<T, T>>(response);
}
return buffer[nextReadIndex++];
});
};
// TODO: If it's a single shot iterator we can optimize memory by cleaning up the buffer after
// reading through the end, but currently we favor code size over this optimization.
// 待办事项:如果这是一个一次性迭代器,我们可以在读到末尾后清理缓冲区以优化内存,但目前我们更倾
// 向于代码大小而不是这种优化。
resolveStream(
response,
id,
iterator ? iterable[ASYNC_ITERATOR]() : iterable,
flightController,
streamState,
);
}
停止流式传输
function stopStream(
response: Response,
id: number,
row: UninitializedModel,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk || chunk.status !== INITIALIZED) {
// We didn't expect not to have an existing stream;
// 我们没想到没有现有的流;
return;
}
if (__DEV__) {
if (--response._pendingChunks === 0) {
// We're no longer waiting for any more chunks. We can release the strong
// reference to the response. We'll regain it if we ask for any more data
// later on.
// 我们不再等待更多的数据块。我们可以释放对响应的强引用。如果我们之后请求更多数据,我们会重新获得它。
response._weakResponse.response = null;
}
}
const streamChunk: InitializedStreamChunk<any> = chunk as any;
const controller = streamChunk.reason;
controller.close(row === '' ? '"$undefined"' : row);
}
解决错误生产
function resolveErrorProd(response: Response): Error {
if (__DEV__) {
// These errors should never make it into a build so we don't need to encode them in codes.json
// 这些错误绝不应该出现在构建中,所以我们不需要在 codes.json 中对它们进行编码
throw new Error(
'resolveErrorProd should never be called in development mode. Use resolveErrorDev instead. This is a bug in React.',
);
}
const error = new Error(
'An error occurred in the Server Components render. The specific message is omitted in production' +
' builds to avoid leaking sensitive details. A digest property is included on this error instance which' +
' may provide additional details about the nature of the error.',
);
error.stack = 'Error: ' + error.message;
return error;
}
解决错误(开发)
function resolveErrorDev(
response: Response,
errorInfo: ReactErrorInfoDev,
): Error {
const name = errorInfo.name;
const message = errorInfo.message;
const stack = errorInfo.stack;
const env = errorInfo.env;
if (!__DEV__) {
// These errors should never make it into a build so we don't need to encode them in codes.json
// 这些错误绝不应该出现在构建中,所以我们不需要在 codes.json 中对它们进行编码
throw new Error(
'resolveErrorDev should never be called in production mode. Use resolveErrorProd instead. This is a bug in React.',
);
}
let error;
const errorOptions =
// We don't serialize Error.cause in prod so we never need to deserialize
// 我们在生产环境中不会序列化 Error.cause,所以我们从不需要反序列化
__DEV__ && 'cause' in errorInfo
? {
cause: reviveModel(
response,
errorInfo.cause as JSONValue,
errorInfo,
'cause',
),
}
: undefined;
const isAggregateError =
typeof AggregateError !== 'undefined' && 'errors' in errorInfo;
const revivedErrors =
// We don't serialize AggregateError.errors in prod so we never need to deserialize
// 我们在生产环境中不会序列化 AggregateError.errors,所以我们从不需要反序列化
__DEV__ && isAggregateError
? reviveModel(
response,
errorInfo.errors as JSONValue,
errorInfo,
'errors',
)
: null;
const callStack = buildFakeCallStack(
response,
stack,
env,
false,
isAggregateError
? AggregateError.bind(
null,
revivedErrors,
message ||
'An error occurred in the Server Components render but no message was provided',
errorOptions,
)
: Error.bind(
null,
message ||
'An error occurred in the Server Components render but no message was provided',
errorOptions,
),
);
let ownerTask: null | ConsoleTask = null;
if (errorInfo.owner != null) {
const ownerRef = errorInfo.owner.slice(1);
// TODO: This is not resilient to the owner loading later in an Error like a debug channel.
// 待办事项:这对于所有者稍后在像调试频道这样的错误中加载是不具弹性的。
// The whole error serialization should probably go through the regular model at least for DEV.
// 整个错误序列化至少在开发环境中可能应该通过常规模型进行。
const owner = getOutlinedModel(response, ownerRef, {}, '', createModel);
if (owner !== null) {
ownerTask = initializeFakeTask(response, owner);
}
}
if (ownerTask === null) {
const rootTask = getRootTask(response, env);
if (rootTask != null) {
error = rootTask.run(callStack);
} else {
error = callStack();
}
} else {
error = ownerTask.run(callStack);
}
(error as any).name = name;
(error as any).environmentName = env;
return error;
}
解析错误模型
function resolveErrorModel(
response: Response,
id: number,
row: UninitializedModel,
streamState: StreamState,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
const errorInfo = JSON.parse(row);
let error;
if (__DEV__) {
error = resolveErrorDev(response, errorInfo);
} else {
error = resolveErrorProd(response);
}
(error as any).digest = errorInfo.digest;
const errorWithDigest: ErrorWithDigest = error as any;
if (!chunk) {
const newChunk: ErroredChunk<any> = createErrorChunk(
response,
errorWithDigest,
);
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, newChunk);
}
chunks.set(id, newChunk);
} else {
if (__DEV__) {
resolveChunkDebugInfo(response, streamState, chunk);
}
triggerErrorOnChunk(response, chunk, errorWithDigest);
}
}
解决提示
function resolveHint<Code extends HintCode>(
response: Response,
code: Code,
model: UninitializedModel,
): void {
const hintModel: HintModel<Code> = parseModel(response, model);
dispatchHint(code, hintModel);
}
创建模拟函数
function createFakeFunction<T>(
name: string,
filename: string,
sourceMap: null | string,
line: number,
col: number,
enclosingLine: number,
enclosingCol: number,
environmentName: string,
): FakeFunction<T> {
// This creates a fake copy of a Server Module. It represents a module that has already
// executed on the server but we re-execute a blank copy for its stack frames on the client.
// 这会创建一个服务器模块的假副本。它表示一个已经在服务器上执行过的模块,但我们会在客户端为其堆
// 栈帧重新执行一个空白副本。
const comment =
'/* This module was rendered by a Server Component. Turn on Source Maps to see the server source. */';
if (!name) {
// An eval:ed function with no name gets the name "eval". We give it something more descriptive.
// 一个通过 eval 创建但没有名字的函数会被命名为 "eval"。我们给它一个更具描述性的名字。
name = '<anonymous>';
}
const encodedName = JSON.stringify(name);
// We generate code where the call is at the line and column of the server executed code.
// 我们生成的代码将在服务器执行代码的行和列上进行调用。
// This allows us to use the original source map as the source map of this fake file to
// point to the original source.
// 这允许我们使用原始源映射作为此虚拟文件的源映射,指向原始源代码。
let code;
// Normalize line/col to zero based.
// 将行/列归一化为从零开始。
if (enclosingLine < 1) {
enclosingLine = 0;
} else {
enclosingLine--;
}
if (enclosingCol < 1) {
enclosingCol = 0;
} else {
enclosingCol--;
}
if (line < 1) {
line = 0;
} else {
line--;
}
if (col < 1) {
col = 0;
} else {
col--;
}
if (line < enclosingLine || (line === enclosingLine && col < enclosingCol)) {
// Protection against invalid enclosing information. Should not happen.
// 防止无效的封闭信息。这种情况不应发生。
enclosingLine = 0;
enclosingCol = 0;
}
if (line < 1) {
// Fit everything on the first line.
// 将所有内容放在第一行。
const minCol = encodedName.length + 3;
let enclosingColDistance = enclosingCol - minCol;
if (enclosingColDistance < 0) {
enclosingColDistance = 0;
}
let colDistance = col - enclosingColDistance - minCol - 3;
if (colDistance < 0) {
colDistance = 0;
}
code =
'({' +
encodedName +
':' +
' '.repeat(enclosingColDistance) +
'_=>' +
' '.repeat(colDistance) +
'_()})';
} else if (enclosingLine < 1) {
// Fit just the enclosing function on the first line.
const minCol = encodedName.length + 3;
let enclosingColDistance = enclosingCol - minCol;
if (enclosingColDistance < 0) {
enclosingColDistance = 0;
}
code =
'({' +
encodedName +
':' +
' '.repeat(enclosingColDistance) +
'_=>' +
'\n'.repeat(line - enclosingLine) +
' '.repeat(col) +
'_()})';
} else if (enclosingLine === line) {
// Fit the enclosing function and callsite on same line.
// 将包含函数和调用位置放在同一行。
let colDistance = col - enclosingCol - 3;
if (colDistance < 0) {
colDistance = 0;
}
code =
'\n'.repeat(enclosingLine - 1) +
'({' +
encodedName +
':\n' +
' '.repeat(enclosingCol) +
'_=>' +
' '.repeat(colDistance) +
'_()})';
} else {
// This is the ideal because we can always encode any position.
// 这是理想情况,因为我们总是可以编码任何位置。
code =
'\n'.repeat(enclosingLine - 1) +
'({' +
encodedName +
':\n' +
' '.repeat(enclosingCol) +
'_=>' +
'\n'.repeat(line - enclosingLine) +
' '.repeat(col) +
'_()})';
}
if (enclosingLine < 1) {
// If the function starts at the first line, we append the comment after.
// 如果函数从第一行开始,我们就在其后附加注释。
code = code + '\n' + comment;
} else {
// Otherwise we prepend the comment on the first line.
// 否则我们在第一行前添加注释。
code = comment + code;
}
if (filename.startsWith('/')) {
// If the filename starts with `/` we assume that it is a file system file
// rather than relative to the current host. Since on the server fully qualified
// stack traces use the file path.
// 如果文件名以 `/` 开头,我们假设它是一个文件系统的文件而不是相对于当前主机的文件。因为在服
// 务器上,完整的栈跟踪使用文件路径。
// TODO: What does this look like on Windows?
// 待办事项:在 Windows 上这会是什么样子?
filename = 'file://' + filename;
}
if (sourceMap) {
// We use the prefix about://React/ to separate these from other files listed in
// the Chrome DevTools. We need a "host name" and not just a protocol because
// otherwise the group name becomes the root folder. Ideally we don't want to
// show these at all but there's two reasons to assign a fake URL.
// 1) A printed stack trace string needs a unique URL to be able to source map it.
// 2) If source maps are disabled or fails, you should at least be able to tell
// which file it was.
// 我们使用前缀 about://React/ 来将这些文件与 Chrome 开发者工具中列出的其他文件区分开。
// 我们需要一个“主机名”,而不仅仅是协议,否则组名会变成根文件夹。理想情况下我们不想显示这
// 些,但有两个原因需要分配一个假 URL。
// 1) 打印的堆栈跟踪字符串需要一个唯一的 URL 才能进行源映射。
// 2) 如果源映射被禁用或失败,你至少应该能够判断出它是哪个文件。
code +=
'\n//# sourceURL=about://React/' +
encodeURIComponent(environmentName) +
'/' +
encodeURI(filename) +
'?' +
fakeFunctionIdx++;
code += '\n//# sourceMappingURL=' + sourceMap;
} else if (filename) {
code += '\n//# sourceURL=' + encodeURI(filename);
} else {
code += '\n//# sourceURL=<anonymous>';
}
let fn: FakeFunction<T>;
try {
fn = (0, eval)(code)[name];
} catch (x) {
// If eval fails, such as if in an environment that doesn't support it,
// we fallback to creating a function here. It'll still have the right
// name but it'll lose line/column number and file name.
// 如果 eval 失败,例如在不支持它的环境中,我们会退而求其次在这里创建一个函数。它仍然会有正
// 确的名称,但会失去行号/列号和文件名。
fn = function (_) {
return _();
};
// Using the usual {[name]: _() => _()}.bind() trick to avoid minifiers
// doesn't work here since this will produce `Object.*` names.
// 使用常见的 {[name]: _() => _()}.bind() 技巧来避免压缩器
// 在这里不起作用,因为这会产生 `Object.*` 名称。
Object.defineProperty(fn, 'name', { value: name });
}
return fn;
}
构建模拟调用栈
function buildFakeCallStack<T>(
response: Response,
stack: ReactStackTrace,
environmentName: string,
useEnclosingLine: boolean,
innerCall: () => T,
): () => T {
let callStack = innerCall;
for (let i = 0; i < stack.length; i++) {
const frame = stack[i];
const frameKey =
frame.join('-') +
'-' +
environmentName +
(useEnclosingLine ? '-e' : '-n');
let fn = fakeFunctionCache.get(frameKey);
if (fn === undefined) {
const [name, filename, line, col, enclosingLine, enclosingCol] = frame;
const findSourceMapURL = response._debugFindSourceMapURL;
const sourceMap = findSourceMapURL
? findSourceMapURL(filename, environmentName)
: null;
fn = createFakeFunction(
name,
filename,
sourceMap,
line,
col,
useEnclosingLine ? line : enclosingLine,
useEnclosingLine ? col : enclosingCol,
environmentName,
);
// TODO: This cache should technically live on the response since the _debugFindSourceMapURL
// function is an input and can vary by response.
// 待办事项:从技术上讲,这个缓存应该存在于响应上,因为 _debugFindSourceMapURL 函数是
// 一个输入,并且可能因响应而异。
fakeFunctionCache.set(frameKey, fn);
}
callStack = fn.bind(null, callStack);
}
return callStack;
}
获取根任务
function getRootTask(
response: Response,
childEnvironmentName: string,
): null | ConsoleTask {
const rootTask = response._debugRootTask;
if (!rootTask) {
return null;
}
if (response._rootEnvironmentName !== childEnvironmentName) {
// If the root most owner component is itself in a different environment than the requested
// environment then we create an extra task to indicate that we're transitioning into it.
// 如果根节点的最上层拥有组件本身处于与请求的环境不同的环境中那么我们会创建一个额外的任务来表
// 示我们正在过渡到该环境。
// Like if one environment just requests another environment.
// 就像一个环境只是请求另一个环境一样。
const createTaskFn = (console as any).createTask.bind(
console,
'"use ' + childEnvironmentName.toLowerCase() + '"',
);
return rootTask.run(createTaskFn);
}
return rootTask;
}
初始化假任务
function initializeFakeTask(
response: Response,
debugInfo: ReactComponentInfo | ReactAsyncInfo | ReactIOInfo,
): null | ConsoleTask {
if (!supportsCreateTask) {
return null;
}
if (debugInfo.stack == null) {
// If this is an error, we should've really already initialized the task.
// If it's null, we can't initialize a task.
// 如果这是一个错误,我们实际上应该已经初始化任务。如果它是空的,我们无法初始化任务。
return null;
}
const cachedEntry = debugInfo.debugTask;
if (cachedEntry !== undefined) {
return cachedEntry;
}
// Workaround for a bug where Chrome Performance tracking uses the enclosing line/column
// instead of the callsite. For ReactAsyncInfo/ReactIOInfo, the only thing we're going
// to use the fake task for is the Performance tracking so we encode the enclosing line/
// column at the callsite to get a better line number. We could do this for Components too
// but we're going to use those for other things too like console logs and it's not worth
// duplicating. If this bug is every fixed in Chrome, this should be set to false.
// 针对 Chrome 性能跟踪使用外部行/列而不是调用位置的 bug 的解决方法对于
// ReactAsyncInfo/ReactIOInfo,我们使用这个伪任务的唯一目的是性能跟踪,所以我们在调用
// 位置编码外部行/列以获得更好的行号。我们也可以对组件做同样处理,但组件还会用于其他用途,如控
// 制台日志,因此不值得重复处理。如果这个 bug 在 Chrome 中修复,这个值应设置为 false。
const useEnclosingLine = debugInfo.key === undefined;
const stack = debugInfo.stack;
const env: string =
debugInfo.env == null ? response._rootEnvironmentName : debugInfo.env;
const ownerEnv: string =
debugInfo.owner == null || debugInfo.owner.env == null
? response._rootEnvironmentName
: debugInfo.owner.env;
const ownerTask =
debugInfo.owner == null
? null
: initializeFakeTask(response, debugInfo.owner);
const taskName =
// This is the boundary between two environments so we'll annotate the task name.
// 这是两个环境之间的边界,所以我们将注释任务名称。
// We assume that the stack frame of the entry into the new environment was done
// from the old environment. So we use the owner's environment as the current.
// 我们假设进入新环境的堆栈帧是由旧环境完成的。因此我们使用所有者的环境作为当前环境。
env !== ownerEnv
? '"use ' + env.toLowerCase() + '"'
: // Some unfortunate pattern matching to refine the type.
// 一些不幸的模式匹配来细化类型。
debugInfo.key !== undefined
? getServerComponentTaskName(debugInfo as any as ReactComponentInfo)
: debugInfo.name !== undefined
? getIOInfoTaskName(debugInfo as any as ReactIOInfo)
: getAsyncInfoTaskName(debugInfo as any as ReactAsyncInfo);
return (debugInfo.debugTask = buildFakeTask(
response,
ownerTask,
stack,
taskName,
ownerEnv,
useEnclosingLine,
));
}
构建模拟任务
function buildFakeTask(
response: Response,
ownerTask: null | ConsoleTask,
stack: ReactStackTrace,
taskName: string,
env: string,
useEnclosingLine: boolean,
): ConsoleTask {
const createTaskFn = (console as any).createTask.bind(console, taskName);
const callStack = buildFakeCallStack(
response,
stack,
env,
useEnclosingLine,
createTaskFn,
);
if (ownerTask === null) {
const rootTask = getRootTask(response, env);
if (rootTask != null) {
return rootTask.run(callStack);
} else {
return callStack();
}
} else {
return ownerTask.run(callStack);
}
}
模拟 JSX 调用点
function fakeJSXCallSite() {
// This extra call frame represents the JSX creation function. We always pop this frame
// off before presenting so it needs to be part of the stack.
// 这个额外的调用帧表示 JSX 创建函数。我们总是在呈现之前将其弹出,所以它需要成为堆栈的一部分。
return new Error('react-stack-top-frame');
}
初始化模拟栈
function initializeFakeStack(
response: Response,
debugInfo: ReactComponentInfo | ReactAsyncInfo | ReactIOInfo,
): void {
const cachedEntry = debugInfo.debugStack;
if (cachedEntry !== undefined) {
return;
}
if (debugInfo.stack != null) {
const stack = debugInfo.stack;
const env = debugInfo.env == null ? '' : debugInfo.env;
debugInfo.debugStack = createFakeJSXCallStackInDEV(response, stack, env);
}
const owner = debugInfo.owner;
if (owner != null) {
// Initialize any owners not yet initialized.
// 初始化尚未初始化的所有者。
initializeFakeStack(response, owner);
if (owner.debugLocation === undefined && debugInfo.debugStack != null) {
// If we are the child of this owner, then the owner should be the bottom frame
// our stack. We can use it as the implied location of the owner.
// 如果是这个所有者的子对象,那么所有者应该是栈底的帧。可以把它作为所有者的隐含位置。
owner.debugLocation = debugInfo.debugStack;
}
}
}
初始化调试信息
function initializeDebugInfo(
response: Response,
debugInfo: ReactDebugInfoEntry,
): ReactDebugInfoEntry {
if (!__DEV__) {
// These errors should never make it into a build so we don't need to encode them in codes.json
// 这些错误永远不应该进入构建,因此我们不需要在 codes.json 中对它们进行编码
throw new Error(
'initializeDebugInfo should never be called in production mode. This is a bug in React.',
);
}
if (debugInfo.stack !== undefined) {
const componentInfoOrAsyncInfo: ReactComponentInfo | ReactAsyncInfo =
debugInfo;
// We eagerly initialize the fake task because this resolving happens outside any
// render phase so we're not inside a user space stack at this point. If we waited
// to initialize it when we need it, we might be inside user code.
// 我们热切地初始化假任务,因为这个解析发生在任何渲染阶段之外,所以此时我们不在用户空间栈中。
// 如果我们等到需要它时再初始化,可能会处于用户代码中。
initializeFakeTask(response, componentInfoOrAsyncInfo);
}
if (debugInfo.owner == null && response._debugRootOwner != null) {
const componentInfoOrAsyncInfo: ReactComponentInfo | ReactAsyncInfo =
debugInfo;
componentInfoOrAsyncInfo.owner = response._debugRootOwner;
// We clear the parsed stack frames to indicate that it needs to be re-parsed from debugStack.
// 我们清除已解析的堆栈帧,以表明它需要从 debugStack 重新解析。
componentInfoOrAsyncInfo.stack = null;
// We override the stack if we override the owner since the stack where the root JSX
// was created on the server isn't very useful but where the request was made is.
// 如果我们重写了所有者,那么我们也会重写堆栈,因为在服务器上创建根 JSX 的堆栈不是很有用,但
// 请求所在的位置才有用。
componentInfoOrAsyncInfo.debugStack = response._debugRootStack;
componentInfoOrAsyncInfo.debugTask = response._debugRootTask;
} else if (debugInfo.stack !== undefined) {
const componentInfoOrAsyncInfo: ReactComponentInfo | ReactAsyncInfo =
debugInfo;
initializeFakeStack(response, componentInfoOrAsyncInfo);
}
if (enableProfilerTimer && enableComponentPerformanceTrack) {
if (typeof debugInfo.time === 'number') {
// Adjust the time to the current environment's time space.
// Since this might be a deduped object, we clone it to avoid
// applying the adjustment twice.
// 将时间调整为当前环境的时间空间。由于这可能是一个去重后的对象,我们克隆它以避免
// 两次应用时间调整。
debugInfo = {
time: debugInfo.time + response._timeOrigin,
};
}
}
return debugInfo;
}
解析调试模型
function resolveDebugModel(
response: Response,
id: number,
json: UninitializedModel,
): void {
const parentChunk = getChunk(response, id);
if (
parentChunk.status === INITIALIZED ||
parentChunk.status === ERRORED ||
parentChunk.status === HALTED ||
parentChunk.status === BLOCKED
) {
// We shouldn't really get debug info late. It's too late to add it after we resolved.
// 我们实际上不应该在晚些时候获取调试信息。在我们解决之后再添加已经太晚了。
return;
}
if (parentChunk.status === RESOLVED_MODULE) {
// We don't expect to get debug info on modules.
// 我们不指望在模块上获得调试信息。
return;
}
const previousChunk = parentChunk._debugChunk;
const debugChunk: ResolvedModelChunk<ReactDebugInfoEntry> =
createResolvedModelChunk(response, json);
// 调试块的链表
debugChunk._debugChunk = previousChunk; // Linked list of the debug chunks
parentChunk._debugChunk = debugChunk;
initializeDebugChunk(response, parentChunk);
if (
__DEV__ &&
(debugChunk as any as SomeChunk<any>).status === BLOCKED &&
(response._debugChannel === undefined ||
!response._debugChannel.hasReadable)
) {
if (json[0] === '"' && json[1] === '$') {
const path = json.slice(2, json.length - 1).split(':');
const outlinedId = parseInt(path[0], 16);
const chunk = getChunk(response, outlinedId);
if (chunk.status === PENDING) {
// We expect the debug chunk to have been emitted earlier in the stream. It might be
// blocked on other things but chunk should no longer be pending.
// 我们预期调试块应该已经在流的早些时候发出。它可能被其他事物阻塞,但块不再应处于待处理
// 状态。
// If it's still pending that suggests that it was referencing an object in the debug
// channel, but no debug channel was wired up so it's missing. In this case we can just
// drop the debug info instead of halting the whole stream.
// 如果它仍然处于待处理状态,这表明它引用了调试通道中的对象,但没有连接调试通道,所以它
// 缺失。在这种情况下,我们可以直接丢弃调试信息,而不是停止整个流。
parentChunk._debugChunk = null;
}
}
}
}
获取当前开发环境堆栈
function getCurrentStackInDEV(): string {
if (__DEV__) {
const owner: null | ReactComponentInfo = currentOwnerInDEV;
if (owner === null) {
return '';
}
return getOwnerStackByComponentInfoInDev(owner);
}
return '';
}
解析控制台条目
function resolveConsoleEntry(
response: Response,
json: UninitializedModel,
): void {
if (!__DEV__) {
// These errors should never make it into a build so we don't need to encode them in codes.json
// 这些错误绝不应该出现在构建中,所以不需要在 codes.json 中对它们进行编码
throw new Error(
'resolveConsoleEntry should never be called in production mode. This is a bug in React.',
);
}
if (!response._replayConsole) {
return;
}
const blockedChunk = response._blockedConsole;
if (blockedChunk == null) {
// If we're not blocked on any other chunks, we can try to eagerly initialize
// this as a fast-path to avoid awaiting them.
// 如果在其他任何块上没有被阻塞,可以尝试急切地初始化这是一个快速路径,以避免等待它们。
const chunk: ResolvedModelChunk<ConsoleEntry> = createResolvedModelChunk(
response,
json,
);
initializeModelChunk(chunk);
const initializedChunk: SomeChunk<ConsoleEntry> = chunk;
if (initializedChunk.status === INITIALIZED) {
replayConsoleWithCallStackInDEV(response, initializedChunk.value);
} else {
chunk.then(
v => replayConsoleWithCallStackInDEV(response, v),
e => {
// Ignore console errors for now. Unnecessary noise.
// 目前忽略控制台错误。不必要的噪音。
},
);
response._blockedConsole = chunk;
}
} else {
// We're still waiting on a previous chunk so we can't enqueue quite yet.
// 我们仍在等待之前的一个块,所以我们还不能排队。
const chunk: SomeChunk<ConsoleEntry> = createPendingChunk(response);
chunk.then(
v => replayConsoleWithCallStackInDEV(response, v),
e => {
// Ignore console errors for now. Unnecessary noise.
// 目前忽略控制台错误。不必要的噪音。
},
);
response._blockedConsole = chunk;
const unblock = () => {
if (response._blockedConsole === chunk) {
// We were still the last chunk so we can now clear the queue and return
// to synchronous emitting.
// 用于同步发射。
response._blockedConsole = null;
}
resolveModelChunk(response, chunk, json);
};
blockedChunk.then(unblock, unblock);
}
}
初始化 IO 信息
function initializeIOInfo(response: Response, ioInfo: ReactIOInfo): void {
if (ioInfo.stack !== undefined) {
initializeFakeTask(response, ioInfo);
initializeFakeStack(response, ioInfo);
}
// Adjust the time to the current environment's time space.
// 将时间调整到当前环境的时间空间。
ioInfo.start += response._timeOrigin;
ioInfo.end += response._timeOrigin;
if (enableComponentPerformanceTrack && response._replayConsole) {
const env = response._rootEnvironmentName;
const promise = ioInfo.value;
if (promise) {
const thenable: Thenable<mixed> = promise as any;
switch (thenable.status) {
case INITIALIZED:
logIOInfo(ioInfo, env, thenable.value);
break;
case ERRORED:
logIOInfoErrored(ioInfo, env, thenable.reason);
break;
default:
// If we haven't resolved the Promise yet, wait to log until have so we can include
// its data in the log.
// 如果尚未解决该 Promise,请等待记录,直到解决,这样我们可以在日志中包含其数据。
promise.then(
logIOInfo.bind(null, ioInfo, env),
logIOInfoErrored.bind(null, ioInfo, env),
);
break;
}
} else {
logIOInfo(ioInfo, env, undefined);
}
}
}
解析 IO 信息
function resolveIOInfo(
response: Response,
id: number,
model: UninitializedModel,
): void {
const chunks = response._chunks;
let chunk = chunks.get(id);
const prevIsInitializingDebugInfo = isInitializingDebugInfo;
isInitializingDebugInfo = true;
try {
if (!chunk) {
chunk = createResolvedModelChunk(response, model);
chunks.set(id, chunk);
initializeModelChunk(chunk);
} else {
resolveModelChunk(response, chunk, model);
if (chunk.status === RESOLVED_MODEL) {
initializeModelChunk(chunk);
}
}
} finally {
isInitializingDebugInfo = prevIsInitializingDebugInfo;
}
if (chunk.status === INITIALIZED) {
initializeIOInfo(response, chunk.value);
} else {
chunk.then(
v => {
initializeIOInfo(response, v);
},
e => {
// Ignore debug info errors for now. Unnecessary noise.
// 暂时忽略调试信息错误。不必要的噪音。
},
);
}
}
合并缓冲区
function mergeBuffer(
buffer: Array<Uint8Array>,
lastChunk: Uint8Array,
): Uint8Array {
const l = buffer.length;
// Count the bytes we'll need
// 计算我们需要的字节数
let byteLength = lastChunk.length;
for (let i = 0; i < l; i++) {
byteLength += buffer[i].byteLength;
}
// Allocate enough contiguous space
// 分配足够的连续空间
const result = new Uint8Array(byteLength);
let offset = 0;
// Copy all the buffers into it.
// 将所有缓冲区复制到其中。
for (let i = 0; i < l; i++) {
const chunk = buffer[i];
result.set(chunk, offset);
offset += chunk.byteLength;
}
result.set(lastChunk, offset);
return result;
}
解析类型化数组
function resolveTypedArray(
response: Response,
id: number,
buffer: Array<Uint8Array>,
lastChunk: Uint8Array,
constructor: any,
bytesPerElement: number,
streamState: StreamState,
): void {
// If the view fits into one original buffer, we just reuse that buffer instead of
// copying it out to a separate copy. This means that it's not always possible to
// transfer these values to other threads without copying first since they may
// share array buffer. For this to work, it must also have bytes aligned to a
// multiple of a size of the type.
// 如果视图适合一个原始缓冲区,我们只需重用该缓冲区,而不是将其复制到单独的副本中。这意味着在不
// 先复制的情况下,将这些值传递到其他线程并不总是可能的,因为它们可能共享数组缓冲区。要使其工
// 作,字节还必须按类型大小的倍数对齐。
const chunk =
buffer.length === 0 && lastChunk.byteOffset % bytesPerElement === 0
? lastChunk
: mergeBuffer(buffer, lastChunk);
// TODO: The transfer protocol of RSC is little-endian. If the client isn't little-endian
// we should convert it instead. In practice big endian isn't really Web compatible so it's
// somewhat safe to assume that browsers aren't going to run it, but maybe there's some SSR
// server that's affected.
// TODO:RSC的传输协议是小端模式。如果客户端不是小端模式,我们应当进行转换。实际上,大端模式并
// 不真正兼容网页,所以可以在一定程度上假设浏览器不会运行它,但也许有一些受影响的SSR服务器。
const view: $ArrayBufferView = new constructor(
chunk.buffer,
chunk.byteOffset,
chunk.byteLength / bytesPerElement,
);
resolveBuffer(response, id, view, streamState);
}
记录组件信息
function logComponentInfo(
response: Response,
root: SomeChunk<any>,
componentInfo: ReactComponentInfo,
trackIdx: number,
startTime: number,
componentEndTime: number,
childrenEndTime: number,
isLastComponent: boolean,
): void {
if (
isLastComponent &&
root.status === ERRORED &&
root.reason !== response._closedReason
) {
// If this is the last component to render before this chunk rejected, then conceptually
// this component errored. If this was a cancellation then it wasn't this component that
// errored.
// 如果这是在此分块被拒绝之前要渲染的最后一个组件,那么从概念上讲这个组件出错了。如果这是取消
// 操作,那么出错的就不是这个组件。
logComponentErrored(
componentInfo,
trackIdx,
startTime,
componentEndTime,
childrenEndTime,
response._rootEnvironmentName,
root.reason,
);
} else {
logComponentRender(
componentInfo,
trackIdx,
startTime,
componentEndTime,
childrenEndTime,
response._rootEnvironmentName,
);
}
}
刷新组件性能
function flushComponentPerformance(
response: Response,
root: SomeChunk<any>,
// 下一个可用曲目
trackIdx: number, // Next available track
// 可用的时间之后,
trackTime: number, // The time after which it is available,
parentEndTime: number,
): ProfilingResult {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
throw new Error(
'flushComponentPerformance should never be called in production mode. This is a bug in React.',
);
}
// Write performance.measure() entries for Server Components in tree order.
// 为服务器组件按树形顺序编写 performance.measure() 条目。
// This must be done at the end to collect the end time from the whole tree.
// 必须在最后执行,以便收集整个树的结束时间。
if (!isArray(root._children)) {
// We have already written this chunk. If this was a cycle, then this will
// be -Infinity and it won't contribute to the parent end time.
// 我们已经写过这段代码了。如果这是一个循环,那么它将是 -Infinity,并且不会对父级结束时间产
// 生影响。
// If this was already emitted by another sibling then we reused the same
// chunk in two places. We should extend the current end time as if it was
// rendered as part of this tree.
// 如果这段代码已经被另一个同级节点发出,那么我们在两个地方重用了同一段代码。我们应该延长当前
// 结束时间,就好像它是作为这个树的一部分被渲染的一样。
const previousResult: ProfilingResult = root._children;
const previousEndTime = previousResult.endTime;
if (
parentEndTime > -Infinity &&
parentEndTime < previousEndTime &&
previousResult.component !== null
) {
// Log a placeholder for the deduped value under this child starting
// from the end of the self time of the parent and spanning until the
// the deduped end.
// 在此子项下记录已去重值的占位符,从父项的自用时间结束开始,直到去重结束。
logDedupedComponentRender(
previousResult.component,
trackIdx,
parentEndTime,
previousEndTime,
response._rootEnvironmentName,
);
}
// Since we didn't bump the track this time, we just return the same track.
// 因为这次我们没有切换轨道,所以直接返回同一个轨道。
previousResult.track = trackIdx;
return previousResult;
}
const children = root._children;
// First find the start time of the first component to know if it was running
// in parallel with the previous.
// 首先找到第一个组件的开始时间,以了解它是否与之前的组件同时运行。
let debugInfo = null;
if (__DEV__) {
debugInfo = root._debugInfo;
if (debugInfo.length === 0 && root.status === 'fulfilled') {
const resolvedValue = resolveLazy(root.value);
if (
typeof resolvedValue === 'object' &&
resolvedValue !== null &&
(isArray(resolvedValue) ||
typeof resolvedValue[ASYNC_ITERATOR] === 'function' ||
resolvedValue.$$typeof === REACT_ELEMENT_TYPE ||
resolvedValue.$$typeof === REACT_LAZY_TYPE) &&
isArray(resolvedValue._debugInfo)
) {
// It's possible that the value has been given the debug info.
// 值可能已经被赋予了调试信息。
// In that case we need to look for it on the resolved value.
// 在这种情况下,我们需要在解析后的值上查找它。
debugInfo = resolvedValue._debugInfo;
}
}
}
if (debugInfo) {
let startTime = 0;
for (let i = 0; i < debugInfo.length; i++) {
const info = debugInfo[i];
if (typeof info.time === 'number') {
startTime = info.time;
}
if (typeof info.name === 'string') {
if (startTime < trackTime) {
// The start time of this component is before the end time of the previous
// component on this track so we need to bump the next one to a parallel track.
// 这个组件的开始时间早于该轨道上前一个组件的结束时间,所以我们需要将下一个组件移到平
// 行轨道上。
trackIdx++;
}
trackTime = startTime;
break;
}
}
for (let i = debugInfo.length - 1; i >= 0; i--) {
const info = debugInfo[i];
if (typeof info.time === 'number') {
if (info.time > parentEndTime) {
parentEndTime = info.time;
// 我们假设最大的数字在末尾。
break; // We assume the highest number is at the end.
}
}
}
}
const result: ProfilingResult = {
track: trackIdx,
endTime: -Infinity,
component: null,
};
root._children = result;
let childrenEndTime = -Infinity;
let childTrackIdx = trackIdx;
let childTrackTime = trackTime;
for (let i = 0; i < children.length; i++) {
const childResult = flushComponentPerformance(
response,
children[i],
childTrackIdx,
childTrackTime,
parentEndTime,
);
if (childResult.component !== null) {
result.component = childResult.component;
}
childTrackIdx = childResult.track;
const childEndTime = childResult.endTime;
if (childEndTime > childTrackTime) {
childTrackTime = childEndTime;
}
if (childEndTime > childrenEndTime) {
childrenEndTime = childEndTime;
}
}
if (debugInfo) {
// Write debug info in reverse order (just like stack traces).
// 以相反的顺序写调试信息(就像堆栈跟踪一样)。
let componentEndTime = 0;
let isLastComponent = true;
let endTime = -1;
let endTimeIdx = -1;
for (let i = debugInfo.length - 1; i >= 0; i--) {
const info = debugInfo[i];
if (typeof info.time !== 'number') {
continue;
}
if (componentEndTime === 0) {
// Last timestamp is the end of the last component.
// 上一个时间戳是最后一个组件的结束。
componentEndTime = info.time;
}
const time = info.time;
if (endTimeIdx > -1) {
// Now that we know the start and end time, we can emit the entries between.
// 现在我们知道了开始和结束时间,我们可以发出之间的条目。
for (let j = endTimeIdx - 1; j > i; j--) {
const candidateInfo = debugInfo[j];
if (typeof candidateInfo.name === 'string') {
if (componentEndTime > childrenEndTime) {
childrenEndTime = componentEndTime;
}
const componentInfo: ReactComponentInfo = candidateInfo;
logComponentInfo(
response,
root,
componentInfo,
trackIdx,
time,
componentEndTime,
childrenEndTime,
isLastComponent,
);
// 上一个组件的结束时间就是下一个组件的开始时间。
componentEndTime = time; // The end time of previous component is the start time of the next.
// Track the root most component of the result for deduping logging.
// 跟踪结果的最根本组件以去重日志。
result.component = componentInfo;
isLastComponent = false;
} else if (
candidateInfo.awaited &&
// Skip awaits on client resources since they didn't block the server component.
// 跳过对客户端资源的 await,因为它们没有阻塞服务器组件。
candidateInfo.awaited.env != null
) {
if (endTime > childrenEndTime) {
childrenEndTime = endTime;
}
const asyncInfo: ReactAsyncInfo = candidateInfo;
const env = response._rootEnvironmentName;
const promise = asyncInfo.awaited.value;
if (promise) {
const thenable: Thenable<mixed> = promise as any;
switch (thenable.status) {
case INITIALIZED:
logComponentAwait(
asyncInfo,
trackIdx,
time,
endTime,
env,
thenable.value,
);
break;
case ERRORED:
logComponentAwaitErrored(
asyncInfo,
trackIdx,
time,
endTime,
env,
thenable.reason,
);
break;
default:
// We assume that we should have received the data by now since this is logged at the
// end of the response stream. This is more sensitive to ordering so we don't wait
// to log it.
// 我们假设我们现在应该已经收到了数据,因为这是在响应流的末尾记录的。
// 这对顺序更敏感,所以我们不会等待就记录它。
logComponentAwait(
asyncInfo,
trackIdx,
time,
endTime,
env,
undefined,
);
break;
}
} else {
logComponentAwait(
asyncInfo,
trackIdx,
time,
endTime,
env,
undefined,
);
}
}
}
} else {
// Anything between the end and now was aborted if it has no end time.
// Either because the client stream was aborted reading it or the server stream aborted.
// 从结束到现在之间的任何内容,如果没有结束时间,都已被中止。
// 要么是因为客户端在读取时流被中止,要么是服务器流被中止。
// 如果我们没有找到其他东西,endTime 就是开始时间。
endTime = time; // If we don't find anything else the endTime is the start time.
for (let j = debugInfo.length - 1; j > i; j--) {
const candidateInfo = debugInfo[j];
if (typeof candidateInfo.name === 'string') {
if (componentEndTime > childrenEndTime) {
childrenEndTime = componentEndTime;
}
const componentInfo: ReactComponentInfo = candidateInfo;
const env = response._rootEnvironmentName;
logComponentAborted(
componentInfo,
trackIdx,
time,
componentEndTime,
childrenEndTime,
env,
);
// 上一个组件的结束时间就是下一个组件的开始时间。
componentEndTime = time; // The end time of previous component is the start time of the next.
// Track the root most component of the result for deduping logging.
// 跟踪结果的最根本组件以去重日志。
result.component = componentInfo;
isLastComponent = false;
} else if (
candidateInfo.awaited &&
// Skip awaits on client resources since they didn't block the server component.
// 跳过对客户端资源的 await,因为它们没有阻塞服务器组件。
candidateInfo.awaited.env != null
) {
// If we don't have an end time for an await, that means we aborted.
// 如果我们没有为 await 设置结束时间,那就意味着我们已中止。
const asyncInfo: ReactAsyncInfo = candidateInfo;
const env = response._rootEnvironmentName;
if (asyncInfo.awaited.end > endTime) {
// 将 I/O 的结束时间作为 await 的结束时间
endTime = asyncInfo.awaited.end; // Take the end time of the I/O as the await end.
}
if (endTime > childrenEndTime) {
childrenEndTime = endTime;
}
logComponentAwaitAborted(asyncInfo, trackIdx, time, endTime, env);
}
}
}
// 下一个条目的结束时间是此时间。
endTime = time; // The end time of the next entry is this time.
endTimeIdx = i;
}
}
result.endTime = childrenEndTime;
return result;
}
刷新初始渲染性能
function flushInitialRenderPerformance(response: Response): void {
if (
enableProfilerTimer &&
enableComponentPerformanceTrack &&
response._replayConsole
) {
const rootChunk = getChunk(response, 0);
if (isArray(rootChunk._children)) {
markAllTracksInOrder();
flushComponentPerformance(response, rootChunk, 0, -Infinity, -Infinity);
}
}
}
处理完整二进制行
function processFullBinaryRow(
response: Response,
streamState: StreamState,
id: number,
tag: number,
buffer: Array<Uint8Array>,
chunk: Uint8Array,
): void {
switch (tag) {
case 65 /* "A" */:
// We must always clone to extract it into a separate buffer instead of just a view.
// 我们必须始终克隆以将其提取到一个单独的缓冲区,而不仅仅是创建一个视图。
resolveBuffer(
response,
id,
mergeBuffer(buffer, chunk).buffer,
streamState,
);
return;
case 79 /* "O" */:
resolveTypedArray(response, id, buffer, chunk, Int8Array, 1, streamState);
return;
case 111 /* "o" */:
resolveBuffer(
response,
id,
buffer.length === 0 ? chunk : mergeBuffer(buffer, chunk),
streamState,
);
return;
case 85 /* "U" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
Uint8ClampedArray,
1,
streamState,
);
return;
case 83 /* "S" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
Int16Array,
2,
streamState,
);
return;
case 115 /* "s" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
Uint16Array,
2,
streamState,
);
return;
case 76 /* "L" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
Int32Array,
4,
streamState,
);
return;
case 108 /* "l" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
Uint32Array,
4,
streamState,
);
return;
case 71 /* "G" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
Float32Array,
4,
streamState,
);
return;
case 103 /* "g" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
Float64Array,
8,
streamState,
);
return;
case 77 /* "M" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
BigInt64Array,
8,
streamState,
);
return;
case 109 /* "m" */:
resolveTypedArray(
response,
id,
buffer,
chunk,
BigUint64Array,
8,
streamState,
);
return;
case 86 /* "V" */:
resolveTypedArray(response, id, buffer, chunk, DataView, 1, streamState);
return;
}
const stringDecoder = response._stringDecoder;
let row = '';
for (let i = 0; i < buffer.length; i++) {
row += readPartialStringChunk(stringDecoder, buffer[i]);
}
row += readFinalStringChunk(stringDecoder, chunk);
processFullStringRow(response, streamState, id, tag, row);
}
处理完整字符串行
function processFullStringRow(
response: Response,
streamState: StreamState,
id: number,
tag: number,
row: string,
): void {
switch (tag) {
case 73 /* "I" */: {
resolveModule(response, id, row, streamState);
return;
}
case 72 /* "H" */: {
const code: HintCode = row[0] as any;
resolveHint(response, code, row.slice(1));
return;
}
case 69 /* "E" */: {
resolveErrorModel(response, id, row, streamState);
return;
}
case 84 /* "T" */: {
resolveText(response, id, row, streamState);
return;
}
case 78 /* "N" */: {
if (
enableProfilerTimer &&
(enableComponentPerformanceTrack || enableAsyncDebugInfo)
) {
// Track the time origin for future debug info. We track it relative
// to the current environment's time space.
// 跟踪时间起点以便将来调试信息使用。我们相对于当前环境的时间空间进行跟踪。
const timeOrigin: number = +row;
response._timeOrigin = timeOrigin - performance.timeOrigin;
return;
}
// Fallthrough to share the error with Debug and Console entries.
// 继续执行以将错误与调试和控制台条目共享。
}
case 68 /* "D" */: {
if (__DEV__) {
resolveDebugModel(response, id, row);
return;
}
// Fallthrough to share the error with Console entries.
// 继续执行以将错误共享到控制台条目。
}
case 74 /* "J" */: {
if (enableProfilerTimer && enableAsyncDebugInfo) {
resolveIOInfo(response, id, row);
return;
}
// Fallthrough to share the error with Console entries.
// 继续执行以将错误共享到控制台条目。
}
case 87 /* "W" */: {
if (__DEV__) {
resolveConsoleEntry(response, row);
return;
}
throw new Error(
'Failed to read a RSC payload created by a development version of React ' +
'on the server while using a production version on the client. Always use ' +
'matching versions on the server and the client.',
);
}
case 82 /* "R" */: {
startReadableStream(response, id, undefined, streamState);
return;
}
// Fallthrough
case 114 /* "r" */: {
startReadableStream(response, id, 'bytes', streamState);
return;
}
// Fallthrough
case 88 /* "X" */: {
startAsyncIterable(response, id, false, streamState);
return;
}
// Fallthrough
case 120 /* "x" */: {
startAsyncIterable(response, id, true, streamState);
return;
}
// Fallthrough
case 67 /* "C" */: {
stopStream(response, id, row);
return;
}
// Fallthrough
default: /* """ "{" "[" "t" "f" "n" "0" - "9" */ {
if (__DEV__ && row === '') {
resolveDebugHalt(response, id);
return;
}
// We assume anything else is JSON.
// 我们假设其他任何内容都是 JSON。
resolveModel(response, id, row, streamState);
return;
}
}
}
解析模型
function parseModel<T>(response: Response, json: UninitializedModel): T {
const rawModel = JSON.parse(json);
// Pass a wrapper object as parentObject to match the original JSON.parse
// reviver behavior, where the root value's reviver receives {"": rootValue}
// as `this`. This ensures parentObject is never null when accessed downstream.
// 将包装对象作为 parentObject 传递,以匹配原始 JSON.parse 的 reviver 行为,根值的
// reviver 会以 {"": rootValue} 作为 `this`。这确保在下游访问时 parentObject 永远不
// 为 null。
return reviveModel(response, rawModel, { '': rawModel }, '');
}
恢复模型
function reviveModel(
response: Response,
value: JSONValue,
parentObject: Object,
key: string,
): any {
if (typeof value === 'string') {
if (value[0] === '$') {
return parseModelString(response, parentObject, key, value);
}
return value;
}
if (typeof value !== 'object' || value === null) {
return value;
}
if (isArray(value)) {
for (let i = 0; i < value.length; i++) {
(value as any)[i] = reviveModel(response, value[i], value, '' + i);
}
if (value[0] === REACT_ELEMENT_TYPE) {
// React element tuple
// React 元素元组
return parseModelTuple(response, value);
}
return value;
}
// Plain object
// 普通对象
for (const k in value) {
if (k === __PROTO__) {
delete (value as any)[k];
} else {
const walked = reviveModel(response, (value as any)[k], value, k);
if (walked !== undefined) {
(value as any)[k] = walked;
} else {
delete (value as any)[k];
}
}
}
return value;
}
获取当前开发环境的所有者
function getCurrentOwnerInDEV(): null | ReactComponentInfo {
return currentOwnerInDEV;
}
类型
流式渲染流控制器
// 流式渲染流控制器
interface FlightStreamController {
enqueueValue(value: any): void;
enqueueModel(json: UninitializedModel): void;
close(json: UninitializedModel): void;
error(error: Error): void;
}
// 未初始化模型
type UninitializedModel = string;
// 分析结果
type ProfilingResult = {
track: number;
endTime: number;
component: null | ReactComponentInfo;
};
行解析器状态
type RowParserState = 0 | 1 | 2 | 3 | 4;
待处理块
// 待处理块
type PendingChunk<T> = {
status: 'pending';
value: null | Array<InitializationReference | ((t: T) => mixed)>;
reason: null | Array<InitializationReference | ((mixed: mixed) => mixed)>;
_children: Array<SomeChunk<any>> | ProfilingResult; // Profiling-only
_debugChunk: null | SomeChunk<ReactDebugInfoEntry>; // DEV-only
_debugInfo: ReactDebugInfo; // DEV-only
then(resolve: (t: T) => mixed, reject?: (mixed: mixed) => mixed): void;
};
// 被阻止的块
type BlockedChunk<T> = {
status: 'blocked';
value: null | Array<InitializationReference | ((t: T) => mixed)>;
reason: null | Array<InitializationReference | ((mixed: mixed) => mixed)>;
_children: Array<SomeChunk<any>> | ProfilingResult; // Profiling-only
_debugChunk: null; // DEV-only
_debugInfo: ReactDebugInfo; // DEV-only
then(resolve: (t: T) => mixed, reject?: (mixed: mixed) => mixed): void;
};
// 已解析模型块
type ResolvedModelChunk<T> = {
status: 'resolved_model';
value: UninitializedModel;
reason: Response;
_children: Array<SomeChunk<any>> | ProfilingResult; // Profiling-only
_debugChunk: null | SomeChunk<ReactDebugInfoEntry>; // DEV-only
_debugInfo: ReactDebugInfo; // DEV-only
then(resolve: (t: T) => mixed, reject?: (mixed: mixed) => mixed): void;
};
// 已解析模块块
type ResolvedModuleChunk<T> = {
status: 'resolved_module';
value: ClientReference<T>;
reason: null;
_children: Array<SomeChunk<any>> | ProfilingResult; // Profiling-only
_debugChunk: null; // DEV-only
_debugInfo: ReactDebugInfo; // DEV-only
then(resolve: (t: T) => mixed, reject?: (mixed: mixed) => mixed): void;
};
// 已初始化的块
type InitializedChunk<T> = {
status: 'fulfilled';
value: T;
reason: null | FlightStreamController;
_children: Array<SomeChunk<any>> | ProfilingResult; // Profiling-only
_debugChunk: null; // DEV-only
_debugInfo: ReactDebugInfo; // DEV-only
then(resolve: (t: T) => mixed, reject?: (mixed: mixed) => mixed): void;
};
// 已初始化的流块
type InitializedStreamChunk<
T extends ReadableStream | $AsyncIterable<any, any, void>,
> = {
status: 'fulfilled';
value: T;
reason: FlightStreamController;
_children: Array<SomeChunk<any>> | ProfilingResult; // Profiling-only
_debugChunk: null; // DEV-only
_debugInfo: ReactDebugInfo; // DEV-only
then(
resolve: (readableStream: ReadableStream) => mixed,
reject?: (mixed: mixed) => mixed,
): void;
};
// 出错的数据块
type ErroredChunk<T> = {
status: 'rejected';
value: null;
reason: mixed;
_children: Array<SomeChunk<any>> | ProfilingResult; // Profiling-only
_debugChunk: null; // DEV-only
_debugInfo: ReactDebugInfo; // DEV-only
then(resolve: (t: T) => mixed, reject?: (mixed: mixed) => mixed): void;
};
// 已停止的块
type HaltedChunk<T> = {
status: 'halted';
value: null;
reason: null;
_children: Array<SomeChunk<any>> | ProfilingResult; // Profiling-only
_debugChunk: null; // DEV-only
_debugInfo: ReactDebugInfo; // DEV-only
then(resolve: (t: T) => mixed, reject?: (mixed: mixed) => mixed): void;
};
// 某块
type SomeChunk<T> =
| PendingChunk<T>
| BlockedChunk<T>
| ResolvedModelChunk<T>
| ResolvedModuleChunk<T>
| InitializedChunk<T>
| ErroredChunk<T>
| HaltedChunk<T>;
回应
type Response = {
_bundlerConfig: ServerConsumerModuleMap;
_serverReferenceConfig: null | ServerManifest;
_moduleLoading: ModuleLoading;
_callServer: CallServerCallback;
_encodeFormAction: void | EncodeFormActionCallback;
_nonce: ?string;
_chunks: Map<number, SomeChunk<any>>;
_stringDecoder: StringDecoder;
_closed: boolean;
_closedReason: mixed;
_allowPartialStream: boolean;
_tempRefs: void | TemporaryReferenceSet; // the set temporary references can be resolved from
_timeOrigin: number; // Profiling-only
_pendingInitialRender: null | TimeoutID; // Profiling-only,
_pendingChunks: number; // DEV-only
_weakResponse: WeakResponse; // DEV-only
_debugRootOwner?: null | ReactComponentInfo; // DEV-only
_debugRootStack?: null | Error; // DEV-only
_debugRootTask?: null | ConsoleTask; // DEV-only
_debugStartTime: number; // DEV-only
_debugEndTime?: number; // DEV-only
_debugIOStarted: boolean; // DEV-only
_debugFindSourceMapURL?: void | FindSourceMapURLCallback; // DEV-only
_debugChannel?: void | DebugChannel; // DEV-only
_blockedConsole?: null | SomeChunk<ConsoleEntry>; // DEV-only
_replayConsole: boolean; // DEV-only
_rootEnvironmentName: string; // DEV-only, the requested environment name.
};
初始化参考
// 初始化参考
type InitializationReference = {
handler: InitializationHandler;
parentObject: Object;
key: string;
map: (
response: Response,
model: any,
parentObject: Object,
key: string,
) => any;
path: Array<string>;
isDebug?: boolean; // DEV-only
};
// 初始化处理程序
type InitializationHandler = {
parent: null | InitializationHandler;
chunk: null | BlockedChunk<any>;
value: any;
reason: any;
deps: number;
errored: boolean;
};
摘要错误
type ErrorWithDigest = Error & { digest?: string };
假函数
type FakeFunction<T> = (() => T) => T;
控制台条目
type ConsoleEntry = [
string,
ReactStackTrace,
null | ReactComponentInfo,
string,
mixed,
];