React flight server config DOM
一、作用
二、导出类型
1. 提示代码
export type HintCode = $Keys<TypeMap>;
2. 提示模型
export type HintModel<T extends HintCode> = TypeMap[T];
3. 提示
export type Hints = Set<string>;
4. 格式上下文
export opaque type FormatContext = number;
三、创建提示
export function createHints(): Hints {
return new Set();
}
四、创建根格式上下文
export function createRootFormatContext(): FormatContext {
return NO_SCOPE;
}
五、获取子格式上下文
export function getChildFormatContext(
parentContext: FormatContext,
type: string,
props: Object,
): FormatContext {
switch (type) {
case 'img':
processImg(props, parentContext);
return parentContext;
case 'link':
processLink(props, parentContext);
return parentContext;
case 'picture':
return parentContext | PICTURE_SCOPE;
case 'noscript':
return parentContext | NOSCRIPT_SCOPE;
default:
return parentContext;
}
}
六、常量
1. 无范围
备注
源码中 67 - 69 行
// 无范围
const NO_SCOPE = /* */ 0b000000;
// 无脚本范围
const NOSCRIPT_SCOPE = /* */ 0b000001;
// 图片范围
const PICTURE_SCOPE = /* */ 0b000010;
七、工具
1. 处理图像
备注
getCrossOriginString()由 crossOriginStrings#getCrossOriginString 实现
function processImg(props: Object, formatContext: FormatContext): void {
// This should mirror the logic of pushImg in ReactFizzConfigDOM.
// 这应该反映 ReactFizzConfigDOM 中 pushImg 的逻辑。
const pictureOrNoScriptTagInScope =
formatContext & (PICTURE_SCOPE | NOSCRIPT_SCOPE);
const { src, srcSet } = props;
if (
props.loading !== 'lazy' &&
(src || srcSet) &&
(typeof src === 'string' || src == null) &&
(typeof srcSet === 'string' || srcSet == null) &&
props.fetchPriority !== 'low' &&
!pictureOrNoScriptTagInScope &&
// We exclude data URIs in src and srcSet since these should not be preloaded
// 我们在 src 和 srcSet 中排除数据 URI,因为这些不应被预加载
!(
typeof src === 'string' &&
src[4] === ':' &&
(src[0] === 'd' || src[0] === 'D') &&
(src[1] === 'a' || src[1] === 'A') &&
(src[2] === 't' || src[2] === 'T') &&
(src[3] === 'a' || src[3] === 'A')
) &&
!(
typeof srcSet === 'string' &&
srcSet[4] === ':' &&
(srcSet[0] === 'd' || srcSet[0] === 'D') &&
(srcSet[1] === 'a' || srcSet[1] === 'A') &&
(srcSet[2] === 't' || srcSet[2] === 'T') &&
(srcSet[3] === 'a' || srcSet[3] === 'A')
)
) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
// 我们有一张挂起感的图片,应该预加载它以优化阻塞显示的加载 `resumableState`
const sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
const crossOrigin = getCrossOriginString(props.crossOrigin);
preload(
// The preload() API requires a href but if we have an imageSrcSet then that will take precedence.
// We already remove the href anyway in both Fizz and Fiber due to a Safari bug so the empty string
// will never actually appear in the DOM.
// preload() API 需要 href,但如果我们有 imageSrcSet,则会优先使用它。
// 无论如何,由于 Safari 的一个错误,我们已经在 Fizz 和 Fiber 中移除了 href,所以空字符串
// 实际上永远不会出现在 DOM 中。
src || '',
'image',
{
imageSrcSet: srcSet,
imageSizes: sizes,
crossOrigin: crossOrigin,
integrity: props.integrity,
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy,
},
);
}
}
2. 处理链接
备注
preload()由 ReactDOMFlightServerHostDispatcher#preload 实现preloadModule()由 ReactDOMFlightServerHostDispatcher#preloadModule 实现
function processLink(props: Object, formatContext: FormatContext): void {
const noscriptTagInScope = formatContext & NOSCRIPT_SCOPE;
const rel = props.rel;
const href = props.href;
if (
noscriptTagInScope ||
props.itemProp != null ||
typeof rel !== 'string' ||
typeof href !== 'string' ||
href === ''
) {
// We shouldn't preload resources that are in noscript or have no configuration.
// 我们不应该预加载位于 noscript 中或没有配置的资源。
return;
}
switch (rel) {
case 'preload': {
preload(href, props.as, {
crossOrigin: props.crossOrigin,
integrity: props.integrity,
nonce: props.nonce,
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy,
imageSrcSet: props.imageSrcSet,
imageSizes: props.imageSizes,
media: props.media,
});
return;
}
case 'modulepreload': {
preloadModule(href, {
as: props.as,
crossOrigin: props.crossOrigin,
integrity: props.integrity,
nonce: props.nonce,
});
return;
}
case 'stylesheet': {
preload(href, 'style', {
crossOrigin: props.crossOrigin,
integrity: props.integrity,
nonce: props.nonce,
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy,
media: props.media,
});
return;
}
}
}
八、类型
1. 未指定优先级
// We use zero to represent the absence of an explicit precedence because it is
// small, smaller than how we encode undefined, and is unambiguous. We could use
// a different tuple structure to encode this instead but this makes the runtime
// cost cheaper by eliminating a type checks in more positions.
// 我们使用零来表示没有明确的优先级,因为它很小,比我们编码未定义的方式还小,而且没有歧义。我们可以使
// 用不同的元组结构来编码它,但这样可以通过在更多位置消除类型检查来降低运行时成本。
// 未指定优先级
type UnspecifiedPrecedence = 0;
type TypeMap = {
// prefetchDNS(href)
// 预取 DNS (href)
D: /* href */ string;
// preconnect(href, options)
// 预连接(href, options)
C: /* href */ string | [/* href */ string, CrossOriginEnum];
// preconnect(href, options)
// 预连接(href, options)
L:
| [/* href */ string, /* as */ string]
| [/* href */ string, /* as */ string, PreloadImplOptions];
m: /* href */ string | [/* href */ string, PreloadModuleImplOptions];
S:
| /* href */ string
| [/* href */ string, /* precedence */ string]
| [
/* href */ string,
/* precedence */ string | UnspecifiedPrecedence,
PreinitStyleOptions,
];
X: /* href */ string | [/* href */ string, PreinitScriptOptions];
M: /* href */ string | [/* href */ string, PreinitModuleScriptOptions];
};