React DOM fizz server edge
一、作用
二、渲染为可读流
备注
startFlowing()由 ReactFizzServer#startFlowing 实现stopFlowing()由 ReactFizzServer#stopFlowing 实现abort()由 ReactFizzServer#abort 实现createResumableState()由 ReactFizzConfigDOM#createResumableState 实现createRequest()由 ReactFizzServer#createRequest 实现createRenderState()由 ReactFizzConfigDOM#createRenderState 实现createRootFormatContext()由 ReactFizzConfigDOM#createRootFormatContext 实现startWork()由 ReactFizzServer#startWork 实现
function renderToReadableStream(
children: ReactNodeList,
options?: Options,
): Promise<ReactDOMServerReadableStream> {
return new Promise((resolve, reject) => {
let onFatalError;
let onAllReady;
const allReady = new Promise<void>((res, rej) => {
onAllReady = res;
onFatalError = rej;
});
function onShellReady() {
const stream: ReactDOMServerReadableStream = new ReadableStream(
{
type: 'bytes',
pull: (controller): ?Promise<void> => {
startFlowing(request, controller);
},
cancel: (reason): ?Promise<void> => {
stopFlowing(request);
abort(request, reason);
},
},
{ highWaterMark: 0 },
) as any;
// TODO: Move to sub-classing ReadableStream.
// 待办事项:迁移到 ReadableStream 的子类化。
stream.allReady = allReady;
resolve(stream);
}
function onShellError(error: mixed) {
// If the shell errors the caller of `renderToReadableStream` won't have access to `allReady`.
// 如果 shell 出错,`renderToReadableStream` 的调用者将无法访问 `allReady`。
// However, `allReady` will be rejected by `onFatalError` as well.
// 然而,`allReady` 也会被 `onFatalError` 拒绝。
// So we need to catch the duplicate, uncatchable fatal error in `allReady` to prevent a `UnhandledPromiseRejection`.
// 因此我们需要在 `allReady` 中捕获重复的、无法捕获的致命错误,以防止 `UnhandledPromiseRejection`。
allReady.catch(() => {});
reject(error);
}
const onHeaders = options ? options.onHeaders : undefined;
let onHeadersImpl;
if (onHeaders) {
onHeadersImpl = (headersDescriptor: HeadersDescriptor) => {
onHeaders(new Headers(headersDescriptor));
};
}
const resumableState = createResumableState(
options ? options.identifierPrefix : undefined,
options ? options.unstable_externalRuntimeSrc : undefined,
options ? options.bootstrapScriptContent : undefined,
options ? options.bootstrapScripts : undefined,
options ? options.bootstrapModules : undefined,
);
const request = createRequest(
children,
resumableState,
createRenderState(
resumableState,
options ? options.nonce : undefined,
options ? options.unstable_externalRuntimeSrc : undefined,
options ? options.importMap : undefined,
onHeadersImpl,
options ? options.maxHeadersLength : undefined,
),
createRootFormatContext(options ? options.namespaceURI : undefined),
options ? options.progressiveChunkSize : undefined,
options ? options.onError : undefined,
onAllReady,
onShellReady,
onShellError,
onFatalError,
options ? options.formState : undefined,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abort(request, (signal as any).reason);
} else {
const listener = () => {
abort(request, (signal as any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
startWork(request);
});
}
三、恢复
备注
startFlowing()由 ReactFizzServer#startFlowing 实现stopFlowing()由 ReactFizzServer#stopFlowing 实现abort()由 ReactFizzServer#abort 实现resumeRequest()由 ReactFizzServer#resumeRequest 实现resumeRenderState()由 ReactFizzConfigDOM#resumeRenderState 实现startWork()由 ReactFizzServer#startWork 实现
function resume(
children: ReactNodeList,
postponedState: PostponedState,
options?: ResumeOptions,
): Promise<ReactDOMServerReadableStream> {
return new Promise((resolve, reject) => {
let onFatalError;
let onAllReady;
const allReady = new Promise<void>((res, rej) => {
onAllReady = res;
onFatalError = rej;
});
function onShellReady() {
const stream: ReactDOMServerReadableStream = new ReadableStream(
{
type: 'bytes',
pull: (controller): ?Promise<void> => {
startFlowing(request, controller);
},
cancel: (reason): ?Promise<void> => {
stopFlowing(request);
abort(request, reason);
},
},
{ highWaterMark: 0 },
) as any;
// TODO: Move to sub-classing ReadableStream.
// 待办事项:迁移到 ReadableStream 的子类化。
stream.allReady = allReady;
resolve(stream);
}
function onShellError(error: mixed) {
// If the shell errors the caller of `renderToReadableStream` won't have access to `allReady`.
// 如果 shell 出错,`renderToReadableStream` 的调用者将无法访问 `allReady`。
// However, `allReady` will be rejected by `onFatalError` as well.
// 然而,`allReady` 也会被 `onFatalError` 拒绝。
// So we need to catch the duplicate, uncatchable fatal error in `allReady` to prevent a `UnhandledPromiseRejection`.
// 因此我们需要在 `allReady` 中捕获重复的、无法捕获的致命错误,以防止 `UnhandledPromiseRejection`。
allReady.catch(() => {});
reject(error);
}
const request = resumeRequest(
children,
postponedState,
resumeRenderState(
postponedState.resumableState,
options ? options.nonce : undefined,
),
options ? options.onError : undefined,
onAllReady,
onShellReady,
onShellError,
onFatalError,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abort(request, (signal as any).reason);
} else {
const listener = () => {
abort(request, (signal as any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
startWork(request);
});
}
四、工具
1. 确保正确的同构 React 版本
在逻辑顶部检验 React 版本是否正确的全局副作用。
ensureCorrectIsomorphicReactVersion();
五、类型
1. 一次性选项
// 一次性选项
type NonceOption =
| string
| {
script?: string;
style?: string;
};
// 选项
type Options = {
identifierPrefix?: string;
namespaceURI?: string;
nonce?: NonceOption;
bootstrapScriptContent?: string;
bootstrapScripts?: Array<string | BootstrapScriptDescriptor>;
bootstrapModules?: Array<string | BootstrapScriptDescriptor>;
progressiveChunkSize?: number;
signal?: AbortSignal;
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string;
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor;
importMap?: ImportMap;
formState?: ReactFormState<any, any> | null;
onHeaders?: (headers: Headers) => void;
maxHeadersLength?: number;
};
// 恢复选项
type ResumeOptions = {
nonce?: NonceOption;
signal?: AbortSignal;
onError?: (error: mixed) => ?string;
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor;
};
// TODO: Move to sub-classing ReadableStream.
// 待办事项:迁移到 ReadableStream 的子类化。
// ReactDOM服务器可读流
type ReactDOMServerReadableStream = ReadableStream & {
allReady: Promise<void>;
};
六、转导
- 从 ReactVersion 转导了
ReactVersion as version