React19源码系列之 API(react-dom)
API之 preconnect
preconnect – React 中文文档
preconnect
函数向浏览器提供一个提示,告诉它应该打开到给定服务器的连接。如果浏览器选择这样做,则可以加快从该服务器加载资源的速度。
preconnect(href)
一、使用例子
import { preconnect } from 'react-dom';function AppRoot() {preconnect("https://example.com");return ...;
}
import { preconnect } from 'react-dom';function CallToAction() {const onClick = () => {preconnect('http://example.com');startWizard();}return (<button onClick={onClick}>Start Wizard</button>);
}
二、源码
function preconnect(href: string, options?: ?PreconnectOptions) {if (typeof href === 'string') {const crossOrigin = options? getCrossOriginString(options.crossOrigin): null;ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.C(/* preconnect */ href, crossOrigin);}
}
ReactDOMSharedInternals.d.C
preconnect
函数的主要功能是发起一个 preconnect
操作,用于提前建立与指定 href
对应的服务器连接。
function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {previousDispatcher.C(/* preconnect */ href, crossOrigin);preconnectAs('preconnect', href, crossOrigin);
}
API之 prefetchDNS
prefetchDNS – React 中文文档
prefetchDNS
函数向浏览器提供一个提示,告诉它应该查找给定服务器的 IP 地址。如果浏览器选择这样做,则可以加快从该服务器加载资源的速度。
一、使用示范
prefetchDNS
允许提前查找期望从中加载资源的服务器的 IP。
prefetchDNS("https://example.com");
二、源码分析
function prefetchDNS(href: string) {if (typeof href === 'string') {ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.D(/* prefetchDNS */ href);}
}
ReactDOMSharedInternals.d.D
prefetchDNS
函数的主要作用是预获取指定 href
对应的 DNS 信息。
function prefetchDNS(href: string) {previousDispatcher.D(/* prefetchDNS */ href);preconnectAs('dns-prefetch', href, null);
}
preconnectAs
preconnectAs
函数用于创建 rel="preconnect"
或 rel="dns-prefetch"
的 link
元素,以提前建立与目标服务器的连接或解析 DNS,从而优化后续资源加载的性能。它会检查是否已存在相同配置的预连接标签,避免重复创建。
function preconnectAs(rel: 'preconnect' | 'dns-prefetch',href: string,crossOrigin: ?CrossOriginEnum,
) {// 获取documentconst ownerDocument = getGlobalDocument();if (ownerDocument && typeof href === 'string' && href) {const limitedEscapedHref =escapeSelectorAttributeValueInsideDoubleQuotes(href);// let key = `link[rel="${rel}"][href="${limitedEscapedHref}"]`;if (typeof crossOrigin === 'string') {key += `[crossorigin="${crossOrigin}"]`;}if (!preconnectsSet.has(key)) {preconnectsSet.add(key);const preconnectProps = {rel, crossOrigin, href};if (null === ownerDocument.querySelector(key)) {const instance = ownerDocument.createElement('link');// 设置link元素的属性setInitialProperties(instance, 'link', preconnectProps);// 标记可提升markNodeAsHoistable(instance);// 往head元素中添加子元素link标签(ownerDocument.head: any).appendChild(instance);}}}
}
类型 | 作用 | 使用场景 |
preconnect | 提前建立完成连接。(包括dns解析、tcp握手和 TLS协商) | 确定会理解加载跨域资源(如字体、API) |
dns-prefetch | 仅提前解析dns,不建立TCP、TLS连接。 | 可能会在未来加载的资源(如广告、第三方内容),减少dns解析延迟。 |
API之 preinit
preinit – React 中文文档
preinit
函数向浏览器提供一个提示,告诉它应该开始下载并执行给定的资源,这可以节省时间。preinit
的脚本在下载完成后执行。预初始化的样式表被插入到文档中,这会使它们立即生效。
preinit(href, options)
参数
href
:字符串,要下载并执行的资源的 URL。options
:对象,可以包含以下属性:as
:必需的字符串,表示资源的类型,可能的值包括script
与style
。precedence
:字符串,与样式表一起使用时必需。指定样式表相对于其他样式表的插入位置。具有较高优先级的样式表可以覆盖具有较低优先级的样式表,可能的值包括reset
、low
、medium
与high
。crossOrigin
:字符串,表示要使用的 CORS 策略,可能的值为anonymous
与use-credentials
。integrity
:字符串,为资源的加密哈希,用于 验证其真实性。nonce
:字符串,表示使用严格内容安全策略时允许资源的 加密随机数。fetchPriority
:字符串,表示建议获取资源的相对优先级,可能的值为auto
(默认值)、high
与low
。
一、使用例子
1、加载script资源
import { preinit } from 'react-dom';function AppRoot() {preinit("https://example.com/script.js", {as: "script"});return ...;
}
2、加载style样式资源
import { preinit } from 'react-dom';function AppRoot() {preinit("https://example.com/style.css", {as: "style", precedence: "medium"});return ...;
}
3、在事件程序中使用加载资源
import { preinit } from 'react-dom';function CallToAction() {const onClick = () => {preinit("https://example.com/wizardStyles.css", {as: "style"});startWizard();}return (<button onClick={onClick}>Start Wizard</button>);
}
二、源码
function preinit(href: string, options: PreinitOptions) {if (typeof href === 'string' && options && typeof options.as === 'string') {// 获取要下载的资源类型const as = options.as;// 获取crossorigin属性的值,可能为use-credentials、空字符串或undefinedconst crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);// 资源加密哈希const integrity =typeof options.integrity === 'string' ? options.integrity : undefined;// 资源优先级const fetchPriority =typeof options.fetchPriority === 'string'? options.fetchPriority: undefined;// 资源为样式表if (as === 'style') {ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.S(/* preinitStyle */href,// 资源链接typeof options.precedence === 'string'? options.precedence: undefined,// 资源优先级{crossOrigin,//integrity,//fetchPriority,//},);// 资源为script脚本} else if (as === 'script') {ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.X(/* preinitScript */ href, {crossOrigin,integrity,fetchPriority,nonce: typeof options.nonce === 'string' ? options.nonce : undefined,//安全策略随机数});}}
}
ReactDOMSharedInternals.d.S(preinitStyle)
preinitStyle
函数的主要功能是预处理样式资源。它会检查指定的样式资源(通过 href
标识)是否已存在于全局文档的样式资源中。如果不存在,会创建一个新的样式资源实例(link
元素),设置其属性,并插入到文档中,同时缓存该资源的相关信息。
function preinitStyle(href: string,precedence: ?string,options?: ?PreinitStyleOptions,
) {previousDispatcher.S(/* preinitStyle */ href, precedence, options);// 获取document对象const ownerDocument = getGlobalDocument();if (ownerDocument && href) {// 获取样式表信息,map类型const styles = getResourcesFromRoot(ownerDocument).hoistableStyles;// 将href解析返回 格式为‘href=xxxxx’const key = getStyleKey(href);precedence = precedence || 'default';// Check if this resource already existslet resource = styles.get(key);if (resource) {// We can early return. The resource exists and there is nothing// more to doreturn;}// 初始化资源const state = {loading: NotLoaded,preload: null,};// Attempt to hydrate instance from DOM// 查询选取器let instance: null | Instance = ownerDocument.querySelector(// `link[rel="stylesheet"][${key}]`getStylesheetSelectorFromKey(key),);if (instance) {// 资源已加载并插入state.loading = Loaded | Inserted;} else {// Construct a new instance and insert it// 处理link相关属性const stylesheetProps = Object.assign(({rel: 'stylesheet',href,'data-precedence': precedence,}: StylesheetProps),options,);const preloadProps = preloadPropsMap.get(key);if (preloadProps) {// 设置stylesheetProps对象中的属性,如title,crossOrigin,refferPolicyadoptPreloadPropsForStylesheet(stylesheetProps, preloadProps);}// 创建link标签const link = (instance = ownerDocument.createElement('link'));//标记markNodeAsHoistable(link);// 设置属性setInitialProperties(link, 'link', stylesheetProps);// 为link设置promise(link: any)._p = new Promise((resolve, reject) => {link.onload = resolve;link.onerror = reject;});// 处理 load 和 error 事件link.addEventListener('load', () => {state.loading |= Loaded;});link.addEventListener('error', () => {state.loading |= Errored;});state.loading |= Inserted;// 插入文档insertStylesheet(instance, precedence, ownerDocument);}// Construct a Resource and cache itresource = {type: 'stylesheet',instance,count: 1,state,};// 缓存资源styles.set(key, resource);return;}
}const preloadPropsMap: Map<string, PreloadProps | PreloadModuleProps> =new Map();
type LoadingState = number;
const NotLoaded = /* */ 0b000;//0 未加载
const Loaded = /* */ 0b001;//1 加载
const Errored = /* */ 0b010;//2 出错
const Settled = /* */ 0b011;//3
const Inserted = /* */ 0b100;//4 插入
type StylesheetProps = {rel: 'stylesheet',href: string,'data-precedence': string,[string]: mixed,
};
function adoptPreloadPropsForStylesheet(stylesheetProps: StylesheetProps,preloadProps: PreloadProps | PreloadModuleProps,
): void {if (stylesheetProps.crossOrigin == null)stylesheetProps.crossOrigin = preloadProps.crossOrigin;if (stylesheetProps.referrerPolicy == null)stylesheetProps.referrerPolicy = preloadProps.referrerPolicy;if (stylesheetProps.title == null) stylesheetProps.title = preloadProps.title;
}
ReactDOMSharedInternals.d.X(preinitScript)
preinitScript
函数的主要功能是预初始化 JavaScript 脚本资源。它会检查指定 src
的脚本是否已存在于文档中,如果不存在则创建并插入一个新的 script
标签,同时缓存该资源的相关信息。该函数支持从现有 DOM 中复用脚本实例,也能处理预加载属性,并确保脚本以异步方式加载。
function preinitScript(src: string, options?: ?PreinitScriptOptions) {previousDispatcher.X(/* preinitScript */ src, options);// 获取document对象const ownerDocument = getGlobalDocument();if (ownerDocument && src) {// 获取资源const scripts = getResourcesFromRoot(ownerDocument).hoistableScripts;// src=xxxxconst key = getScriptKey(src);// Check if this resource already existslet resource = scripts.get(key);if (resource) {// We can early return. The resource exists and there is nothing// more to doreturn;}// Attempt to hydrate instance from DOMlet instance: null | Instance = ownerDocument.querySelector(getScriptSelectorFromKey(key),);if (!instance) {// Construct a new instance and insert itconst scriptProps = Object.assign(({src,async: true,// 异步加载}: ScriptProps),options,);// Adopt certain preload propsconst preloadProps = preloadPropsMap.get(key);if (preloadProps) {// 设置属性integrity、referrerPolicy、crossOriginadoptPreloadPropsForScript(scriptProps, preloadProps);}// 创建script标签instance = ownerDocument.createElement('script');// 打标记markNodeAsHoistable(instance);// 设置属性setInitialProperties(instance, 'link', scriptProps);// 添加(ownerDocument.head: any).appendChild(instance);}// Construct a Resource and cache itresource = {type: 'script',instance,count: 1,state: null,};scripts.set(key, resource);return;}
}
function getScriptSelectorFromKey(key: string): string {return 'script[async]' + key;
}
function adoptPreloadPropsForScript(scriptProps: ScriptProps,preloadProps: PreloadProps | PreloadModuleProps,
): void {if (scriptProps.crossOrigin == null)scriptProps.crossOrigin = preloadProps.crossOrigin;if (scriptProps.referrerPolicy == null)scriptProps.referrerPolicy = preloadProps.referrerPolicy;if (scriptProps.integrity == null)scriptProps.integrity = preloadProps.integrity;
}
API之 preinitModule
preinitModule – React 中文文档
preinitModule
函数向浏览器提供一个提示,告诉它应该开始下载并执行给定的模块,这可以节省时间。预初始化的模块在下载完成后执行。
preinitModule(href, options)
参数
href
:字符串,要下载并执行的模块的 URL。options
:对象,可以包含以下属性:as
:必需的字符串,只能是script
。crossOrigin
:字符串,表示要使用的 CORS 策略,可能的值为anonymous
与use-credentials
。integrity
:字符串,为资源的加密哈希,用于 验证其真实性。nonce
:字符串,表示使用严格内容安全策略时允许资源的 加密随机数。
一、使用示范
import { preinitModule } from 'react-dom';function AppRoot() {preinitModule("https://example.com/module.js", {as: "script"});return ...;
}
import { preinitModule } from 'react-dom';function CallToAction() {const onClick = () => {preinitModule("https://example.com/module.js", {as: "script"});startWizard();}return (<button onClick={onClick}>Start Wizard</button>);
}
二、源码分析
function preinitModule(href: string, options?: ?PreinitModuleOptions) {if (typeof href === 'string') {if (typeof options === 'object' && options !== null) {if (options.as == null || options.as === 'script') {const crossOrigin = getCrossOriginStringAs(options.as,options.crossOrigin,);ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.M(/* preinitModuleScript */ href, {crossOrigin,integrity:typeof options.integrity === 'string'? options.integrity: undefined,nonce:typeof options.nonce === 'string' ? options.nonce : undefined,});}} else if (options == null) {ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.M(/* preinitModuleScript */ href);}}
}
ReactDOMSharedInternals.d.M
preinitModuleScript
函数的核心功能是预初始化 ES6 模块脚本资源。它会检查指定 src
的模块脚本是否已存在于文档中,如果不存在则创建并插入一个新的 script
标签(设置为 type="module"
),同时缓存该资源的相关信息。
function preinitModuleScript(src: string,options?: ?PreinitModuleScriptOptions,
) {previousDispatcher.M(/* preinitModuleScript */ src, options);// 获取document对象const ownerDocument = getGlobalDocument();if (ownerDocument && src) {// 获取资源const scripts = getResourcesFromRoot(ownerDocument).hoistableScripts;const key = getScriptKey(src);// Check if this resource already existslet resource = scripts.get(key);if (resource) {// We can early return. The resource exists and there is nothing// more to doreturn;}// Attempt to hydrate instance from DOMlet instance: null | Instance = ownerDocument.querySelector(getScriptSelectorFromKey(key),);if (!instance) {// Construct a new instance and insert itconst scriptProps = Object.assign(({src,async: true,//异步加载type: 'module',// 模块}: ScriptProps),options,);// Adopt certain preload propsconst preloadProps = preloadPropsMap.get(key);if (preloadProps) {// 如果存在预加载属性 preloadProps,则将其应用到脚本属性中(如 integrity、referrerPolicy、crossOrigin 等)。adoptPreloadPropsForScript(scriptProps, preloadProps);}instance = ownerDocument.createElement('script');// 标记可提升markNodeAsHoistable(instance);setInitialProperties(instance, 'link', scriptProps);(ownerDocument.head: any).appendChild(instance);}// Construct a Resource and cache itresource = {type: 'script',instance,count: 1,state: null,};scripts.set(key, resource);return;}
}
API之 preload
preload – React 中文文档
preload
函数的主要作用是对资源进行预加载操作。它会先对传入的参数进行有效性检查,确保参数类型符合要求,然后触发资源的预加载。
preload(href, options)
参数
- href:字符串,要下载的资源的 URL。
- options:对象,可以包含以下属性:
as
:必需的字符串,表示资源的类型,可能的值 包括 audio、document、embed、fetch、font、image、object、script、style、track、video 与 worker。crossOrigin
:字符串,表示要使用的 CORS 策略,可能的值为 anonymous 与 use-credentials。当 as 设置为 "fetch" 时是必需的。referrerPolicy
:字符串,表示在获取时发送的 referer 请求头,可能的值为 no-referrer-when-downgrade(默认值)、no-referrer、origin、origin-when-cross-origin 与 unsafe-url。integrity
:字符串,为资源的加密哈希,用于 验证其真实性。type
:字符串,表示资源的 MIME 类型。nonce
:字符串,表示使用严格内容安全策略时允许资源的 加密随机数。fetchPriority
:字符串,为获取资源建议的相对优先级,可能的值为 auto(默认值)、high 与 low。imageSrcSet
:字符串,仅与 as: "image" 一起使用,用于指定 图像的源集。imageSizes
:字符串,仅与 as: "image" 一起使用,用于指定 图像的尺寸。
一、使用示范
- 渲染时进行预加载
- 在事件处理程序中预加载
preload 可以预获取期望使用的资源,比如样式表、字体或外部脚本。
preload("https://example.com/font.woff2", {as: "font"});
import { preload } from 'react-dom';function AppRoot() {preload("https://example.com/style.css", {as: "style"});preload("https://example.com/font.woff2", {as: "font"});return ...;
}
二、源码
function preload(href: string, options: PreloadOptions) {if (typeof href === 'string' &&typeof options === 'object' &&options !== null &&typeof options.as === 'string') {// as:从 options 对象中提取 as 属性的值,该属性通常用于指定预加载资源的类型,如 'script'、'style' 等。const as = options.as;// 调用 getCrossOriginStringAs 函数,根据 as 和 options.crossOrigin 生成跨域相关的字符串。const crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.L(/* preload */ href, as, {crossOrigin,integrity:typeof options.integrity === 'string' ? options.integrity : undefined,nonce: typeof options.nonce === 'string' ? options.nonce : undefined,type: typeof options.type === 'string' ? options.type : undefined,fetchPriority:typeof options.fetchPriority === 'string'? options.fetchPriority: undefined,referrerPolicy:typeof options.referrerPolicy === 'string'? options.referrerPolicy: undefined,imageSrcSet:typeof options.imageSrcSet === 'string'? options.imageSrcSet: undefined,imageSizes:typeof options.imageSizes === 'string'? options.imageSizes: undefined,media: typeof options.media === 'string' ? options.media : undefined,});}
}
ReactDOMSharedInternals.d.L
preload
函数主要用于预加载资源,通过创建 rel="preload"
的 link
元素来实现。它会根据资源的类型(as
)和相关选项(options
)生成合适的选择器和属性,检查资源是否已存在于文档中,若不存在则插入新的 link
元素进行预加载,同时处理 Safari 中关于图片 imageSrcSet
的特殊情况。
function preload(href: string, as: string, options?: ?PreloadImplOptions) {previousDispatcher.L(/* preload */ href, as, options);// 获取document对象const ownerDocument = getGlobalDocument();if (ownerDocument && href && as) {// 预加载选择器let preloadSelector = `link[rel="preload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes(as,)}"]`;// 当资源类型为image时,处理if (as === 'image') {if (options && options.imageSrcSet) {preloadSelector += `[imagesrcset="${escapeSelectorAttributeValueInsideDoubleQuotes(options.imageSrcSet,)}"]`;if (typeof options.imageSizes === 'string') {preloadSelector += `[imagesizes="${escapeSelectorAttributeValueInsideDoubleQuotes(options.imageSizes,)}"]`;}} else {preloadSelector += `[href="${escapeSelectorAttributeValueInsideDoubleQuotes(href,)}"]`;}} else {// 对于非图片类型的资源,直接向选择器中添加 href 属性值。preloadSelector += `[href="${escapeSelectorAttributeValueInsideDoubleQuotes(href,)}"]`;}let key = preloadSelector;switch (as) {case 'style':// 当资源类型为 style 时,将 key 更新为 getStyleKey(href) 的返回值key = getStyleKey(href);break;case 'script':// 当资源类型为 script 时,将 key 更新为 getScriptKey(href) 的返回值key = getScriptKey(href);break;}if (!preloadPropsMap.has(key)) {// 创建预加载属性const preloadProps = Object.assign(({rel: 'preload',href:as === 'image' && options && options.imageSrcSet ? undefined : href,as,}: PreloadProps),options,);preloadPropsMap.set(key, preloadProps);if (null === ownerDocument.querySelector(preloadSelector)) {if (as === 'style' &&ownerDocument.querySelector(getStylesheetSelectorFromKey(key))) {// 若资源类型为 style 且已存在对应样式表(通过 getStylesheetSelectorFromKey(key) 检查),则返回,不进行预加载。// We already have a stylesheet for this key. We don't need to preload it.return;} else if (as === 'script' &&ownerDocument.querySelector(getScriptSelectorFromKey(key))) {// 若资源类型为 script 且已存在对应脚本(通过 getScriptSelectorFromKey(key) 检查),则返回,不进行预加载。// We already have a stylesheet for this key. We don't need to preload it.return;}// 创建link元素const instance = ownerDocument.createElement('link');// 设置初始属性setInitialProperties(instance, 'link', preloadProps);// 标记可提升markNodeAsHoistable(instance);// 往head标签中插入子节点(ownerDocument.head: any).appendChild(instance);}}}
}
API之 preloadModule
preloadModule – React 中文文档
preloadModule
可以急切地预获取期望使用的 ESM 模块。
preloadModule("https://example.com/module.js", {as: "script"});
参考语法
preloadModule(href, options)
参数
- href:字符串,要下载的资源的 URL。
- options:对象,可以包含以下属性:
as
:必需的字符串,表示资源的类型,可能的值 包括 audio、document、embed、fetch、font、image、object、script、style、track、video 与 worker。crossOrigin
:字符串,表示要使用的 CORS 策略,可能的值为 anonymous 与 use-credentials。当 as 设置为 "fetch" 时是必需的。integrity
:字符串,为资源的加密哈希,用于 验证其真实性。nonce
:字符串,表示使用严格内容安全策略时允许资源的 加密随机数。
一、使用示范
import { preloadModule } from 'react-dom';function CallToAction() {const onClick = () => {preloadModule("https://example.com/module.js", {as: "script"});startWizard();}return (<button onClick={onClick}>Start Wizard</button>);
}
二、源码
function preloadModule(href: string, options?: ?PreloadModuleOptions) {if (typeof href === 'string') {if (options) {const crossOrigin = getCrossOriginStringAs(options.as,options.crossOrigin,);ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.m(/* preloadModule */ href, {as:typeof options.as === 'string' && options.as !== 'script'? options.as: undefined,crossOrigin,integrity:typeof options.integrity === 'string'? options.integrity: undefined,});} else {ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */.m(/* preloadModule */ href);}}}
ReactDOMSharedInternals.d.m
preloadModule
函数用于预加载 ES6 模块资源,通过创建 rel="modulepreload"
的 link
元素来实现。它会根据资源类型(as
)和相关选项生成合适的选择器和属性,检查资源是否已存在于文档中,若不存在则插入新的 link
元素进行预加载。
function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {previousDispatcher.m(/* preloadModule */ href, options);// 获取document对象const ownerDocument = getGlobalDocument();if (ownerDocument && href) {const as =options && typeof options.as === 'string' ? options.as : 'script';// 生成预加载选择器 preloadSelector,包含 rel="modulepreload"、资源类型 as 和资源链接 href。const preloadSelector = `link[rel="modulepreload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes(as,)}"][href="${escapeSelectorAttributeValueInsideDoubleQuotes(href)}"]`;let key = preloadSelector;switch (as) {case 'audioworklet':case 'paintworklet':case 'serviceworker':case 'sharedworker':case 'worker':case 'script': {key = getScriptKey(href);break;}}if (!preloadPropsMap.has(key)) {const props: PreloadModuleProps = Object.assign(({rel: 'modulepreload',href,}: PreloadModuleProps),options,);preloadPropsMap.set(key, props);if (null === ownerDocument.querySelector(preloadSelector)) {switch (as) {case 'audioworklet':case 'paintworklet':case 'serviceworker':case 'sharedworker':case 'worker':case 'script': {if (ownerDocument.querySelector(getScriptSelectorFromKey(key))) {return;}}}const instance = ownerDocument.createElement('link');setInitialProperties(instance, 'link', props);markNodeAsHoistable(instance);(ownerDocument.head: any).appendChild(instance);}}}
}
工具函数之 getCrossOriginStringAs
返回值只有三种情况:use-credentials、空字符串、undefined。
function getCrossOriginStringAs(as: ?string,// 资源类型input: ?string,// crossorigin属性值
): ?CrossOriginString {// 字体类型返回空字符串if (as === 'font') {return '';}if (typeof input === 'string') {// 如果crossorigin值为use-credentials,否则返回空字符串return input === 'use-credentials' ? input : '';}return undefined;
}
工具函数之 getCrossOriginString
返回值只有三种情况:use-credentials、空字符串、undefined。
function getCrossOriginString(input: ?string): ?CrossOriginString {if (typeof input === 'string') {return input === 'use-credentials' ? input : '';}return undefined;
}
工具函数之 getGlobalDocument
获取document对象。
const globalDocument = typeof document === 'undefined' ? null : document;function getGlobalDocument(): ?Document {return globalDocument;
}
工具函数之 getResourcesFromRoot
获取资源,当资源不存在时进行初始化操作后返回。
function getResourcesFromRoot(root: HoistableRoot): RootResources {// 获取资源let resources = (root: any)[internalRootNodeResourcesKey];if (!resources) {// 设置资源resources = (root: any)[internalRootNodeResourcesKey] = {hoistableStyles: new Map(),hoistableScripts: new Map(),};}return resources;
}const internalRootNodeResourcesKey = '__reactResources$' + randomKey;
export type HoistableRoot = Document | ShadowRoot;
export type HoistableRoot = Document | ShadowRoot;
export type RootResources = {hoistableStyles: Map<string, StyleResource>,hoistableScripts: Map<string, ScriptResource>,
};
工具函数之 markNodeAsHoistable
设置节点为可提升。
function markNodeAsHoistable(node: Node) {(node: any)[internalHoistableMarker] = true;
}const internalHoistableMarker = '__reactMarker$' + randomKey;
工具函数之 getStyleKey
function getStyleKey(href: string) {const limitedEscapedHref =escapeSelectorAttributeValueInsideDoubleQuotes(href);return `href="${limitedEscapedHref}"`;
}
工具函数之 getScriptKey
function getScriptKey(src: string): string {const limitedEscapedSrc = escapeSelectorAttributeValueInsideDoubleQuotes(src);return `[src="${limitedEscapedSrc}"]`;
}
工具函数之 escapeSelectorAttributeValueInsideDoubleQuotes
// 正则表达式用于匹配字符串中的换行符(\n)、双引号(")和反斜杠(\\)。g 标志表示全局匹配
const escapeSelectorAttributeValueInsideDoubleQuotesRegex = /[\n\"\\]/g;function escapeSelectorAttributeValueInsideDoubleQuotes(value,
) {return value.replace(escapeSelectorAttributeValueInsideDoubleQuotesRegex,ch => '\\' + ch.charCodeAt(0).toString(16) + ' ',);
}
const input = 'This is a string with "quotes" and \n newlines and \\ backslashes.';
const escaped = escapeSelectorAttributeValueInsideDoubleQuotes(input);
console.log(escaped);
// This is a string with \22 quotes\22 and \a newlines and \5c backslashes.
工具函数之 getStylesheetSelectorFromKey
function getStylesheetSelectorFromKey(key: string) {return `link[rel="stylesheet"][${key}]`;
}
工具函数之 insertStylesheet
insertStylesheet
函数的主要作用是将样式元素(link
或 style
)按照特定的优先级规则插入到文档中。它会根据元素的 data-precedence
属性值来确定插入位置,确保相同优先级的样式元素相邻排列,并且按照优先级从高到低的顺序排列在文档中。
function insertStylesheet(instance: Element,precedence: string,// 优先级root: HoistableRoot,
): void {// 使用 querySelectorAll 查找根节点 root 下所有带有 data-precedence 属性的样式元素(包括 link 和 style)。const nodes = root.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]',);// 最后一个const last = nodes.length ? nodes[nodes.length - 1] : null;let prior = last;for (let i = 0; i < nodes.length; i++) {const node = nodes[i];// 获取优先级const nodePrecedence = node.dataset.precedence;// 如果当前元素的 data-precedence 与目标优先级 precedence 相同,则更新 prior 为该元素。if (nodePrecedence === precedence) {prior = node;} else if (prior !== last) {// 退出循环break;}}if (prior) {// 新元素插入到 prior 的下一个兄弟节点之前((prior.parentNode: any): Node).insertBefore(instance, prior.nextSibling);} else {const parent =root.nodeType === DOCUMENT_NODE// 将元素插入到文档的 head 中作为第一个子元素? ((((root: any): Document).head: any): Element)// 插入到 Shadow DOM 的根节点中作为第一个子元素: ((root: any): ShadowRoot);parent.insertBefore(instance, parent.firstChild);}
}