React19源码系列之 API(react-dom)

news2025/7/18 3:18:52

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,
) {
  // 获取document
  const 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:必需的字符串,表示资源的类型,可能的值包括 scriptstyle
    • precedence:字符串,与样式表一起使用时必需。指定样式表相对于其他样式表的插入位置。具有较高优先级的样式表可以覆盖具有较低优先级的样式表,可能的值包括 resetlowmediumhigh
    • crossOrigin:字符串,表示要使用的 CORS 策略,可能的值为 anonymoususe-credentials
    • integrity:字符串,为资源的加密哈希,用于 验证其真实性。
    • nonce:字符串,表示使用严格内容安全策略时允许资源的 加密随机数。
    • fetchPriority:字符串,表示建议获取资源的相对优先级,可能的值为 auto(默认值)、highlow

一、使用例子

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、空字符串或undefined
    const 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 exists
    let resource = styles.get(key);
    if (resource) {
      // We can early return. The resource exists and there is nothing
      // more to do
      return;
    }

    // 初始化资源
    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,refferPolicy
        adoptPreloadPropsForStylesheet(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 it
    resource = {
      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=xxxx
    const key = getScriptKey(src);

    // Check if this resource already exists
    let resource = scripts.get(key);
    if (resource) {
      // We can early return. The resource exists and there is nothing
      // more to do
      return;
    }

    // Attempt to hydrate instance from DOM
    let instance: null | Instance = ownerDocument.querySelector(
      getScriptSelectorFromKey(key),
    );
    if (!instance) {
      // Construct a new instance and insert it
      const scriptProps = Object.assign(
        ({
          src,
          async: true,// 异步加载
        }: ScriptProps),
        options,
      );
      // Adopt certain preload props
      const preloadProps = preloadPropsMap.get(key);
      if (preloadProps) {
        // 设置属性integrity、referrerPolicy、crossOrigin
        adoptPreloadPropsForScript(scriptProps, preloadProps);
      }
      // 创建script标签
      instance = ownerDocument.createElement('script');
      // 打标记
      markNodeAsHoistable(instance);
      // 设置属性
      setInitialProperties(instance, 'link', scriptProps);
      // 添加
      (ownerDocument.head: any).appendChild(instance);
    }

    // Construct a Resource and cache it
    resource = {
      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 策略,可能的值为 anonymoususe-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 exists
    let resource = scripts.get(key);
    if (resource) {
      // We can early return. The resource exists and there is nothing
      // more to do
      return;
    }

    // Attempt to hydrate instance from DOM
    let instance: null | Instance = ownerDocument.querySelector(
      getScriptSelectorFromKey(key),
    );
    if (!instance) {
      // Construct a new instance and insert it
      const scriptProps = Object.assign(
        ({
          src,
          async: true,//异步加载
          type: 'module',// 模块
        }: ScriptProps),
        options,
      );
      // Adopt certain preload props
      const 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 it
    resource = {
      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" 一起使用,用于指定 图像的尺寸。

 一、使用示范

  1. 渲染时进行预加载
  2. 在事件处理程序中预加载

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 函数的主要作用是将样式元素(linkstyle)按照特定的优先级规则插入到文档中。它会根据元素的 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);
      }
    }

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2379426.html

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

    相关文章

    supervisorctl守护进程

    supervisorctl守护进程 1 安装 # ubuntu安装&#xff1a; sudo apt-get install supervisor 完成后可以在/etc/supervisor文件夹&#xff0c;找到supervisor.conf。 如果没有的话可以用如下命令创建配置文件&#xff08;注意必须存在/etc/supervisor这个文件夹&#xff09; s…

    下载的旧版的jenkins,为什么没有旧版的插件

    下载的旧版的jenkins&#xff0c;为什么没有旧版的插件&#xff0c;别急 我的jenkins版本&#xff1a; 然后我去找对应的插件 https://updates.jenkins.io/download/plugins/ 1、Maven Integration plugin&#xff1a; Maven 集成管理插件。 然后点击及下载成功 然后 注意&…

    【ALINX 实战笔记】FPGA 大神 Adam Taylor 使用 ChipScope 调试 AMD Versal 设计

    本篇文章来自 FPGA 大神、Ardiuvo & Hackster.IO 知名博主 Adam Taylor。在这里感谢 Adam Taylor 对 ALINX 产品的关注与使用。为了让文章更易阅读&#xff0c;我们在原文的基础上作了一些灵活的调整。原文链接已贴在文章底部&#xff0c;欢迎大家在评论区友好互动。 在上篇…

    【PostgreSQL数据分析实战:从数据清洗到可视化全流程】附录-A. PostgreSQL常用函数速查表

    👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 PostgreSQL常用函数速查表:从数据清洗到分析的全场景工具集引言一、字符串处理函数1.1 基础操作函数1.2 模式匹配函数(正则表达式)二、数值计算函数2.1 基础运算函数2.2 统计相关函数三、日期与时间函…

    【时空图神经网络 交通】相关模型2:STSGCN | 时空同步图卷积网络 | 空间相关性,时间相关性,空间-时间异质性

    注:仅学习使用~ 前情提要: 【时空图神经网络 & 交通】相关模型1:STGCN | 完全卷积结构,高效的图卷积近似,瓶颈策略 | 时间门控卷积层:GLU(Gated Linear Unit),一种特殊的非线性门控单元目录 STSGCN-2020年1.1 背景1.2 模型1.2.1 问题背景:现有模型存在的问题1.2…

    docker 学习记录

    docker pull nginx docker 将本地nginx快照保存到当前文件夹下 docker save -o nginx.tar nginx:latestdocker 将本地nginx 加载 docker load -i nginx.tar docker运行nginx在80端口 docker run --name dnginx -p 80:80 -d nginxredis启动 docker run --name mr -p 6379:6379 -…

    南京邮电大学金工实习答案

    一、金工实习的定义 金工实习是机械类专业学生一项重要的实践课程&#xff0c;它绝非仅仅只是理论知识在操作层面的简单验证&#xff0c;而是一个全方位培养学生综合实践能力与职业素养的系统工程。从本质上而言&#xff0c;金工实习是学生走出教室&#xff0c;亲身踏入机械加…

    世界模型+大模型+自动驾驶 论文小汇总

    最近看了一些论文&#xff0c;懒得一个个写博客了&#xff0c;直接汇总起来 文章目录 大模型VLM-ADVLM-E2EOpenDriveVLAFASIONAD&#xff1a;自适应反馈的类人自动驾驶中快速和慢速思维融合系统快系统慢系统快慢结合 世界模型End-to-End Driving with Online Trajectory Evalu…

    C++函数三剑客:缺省参数·函数重载·引用的高效编程指南

    前引&#xff1a;在C编程中&#xff0c;缺省参数、函数重载、引用是提升代码简洁性、复用性和效率的三大核心机制。它们既能减少冗杂的代码&#xff0c;又能增强接口设计的灵活性。本文将通过清晰的理论解析与实战案列&#xff0c;带你深入理解这三者的设计思想、使用场景以及闭…

    SWUST数据结构下半期实验练习题

    1068: 图的按录入顺序深度优先搜索 #include"iostream" using namespace std; #include"cstring" int visited[100]; char s[100]; int a[100][100]; int n; void dfs(int k,int n) {if(visited[k]0){visited[k]1;cout<<s[k];for(int i0;i<n;i){i…

    机器学习 Day18 Support Vector Machine ——最优美的机器学习算法

    1.问题导入&#xff1a; 2.SVM定义和一些最优化理论 2.1SVM中的定义 2.1.1 定义 SVM 定义&#xff1a;SVM&#xff08;Support Vector Machine&#xff0c;支持向量机&#xff09;核心是寻找超平面将样本分成两类且间隔最大 。它功能多样&#xff0c;可用于线性或非线性分类…

    答题pk小程序道具卡的获取与应用

    道具卡是答题PK小程序中必不可少的一项增加趣味性的辅助应用&#xff0c;那么道具卡是如何获取与应用的呢&#xff0c;接下来我们来揭晓答案&#xff1a; 一、道具卡的获取&#xff1a; 签到获取&#xff1a;在每日签到中签到不仅可获得当日的签到奖励积分&#xff0c;同时连…

    leetcode3265. 统计近似相等数对 I-medium

    1 题目&#xff1a;统计近似相等数对 I 官方标定难度&#xff1a;中 给你一个正整数数组 nums 。 如果我们执行以下操作 至多一次 可以让两个整数 x 和 y 相等&#xff0c;那么我们称这个数对是 近似相等 的&#xff1a; 选择 x 或者 y 之一&#xff0c;将这个数字中的两个…

    【架构篇】代码组织结构设计

    代码组织结构设计&#xff1a;模块化分层与高效协作实践 摘要 本文以Java项目为例&#xff0c;解析后端代码组织的标准化结构&#xff0c;涵盖模块划分原则、依赖管理策略及实际应用场景。通过模块化设计提升代码可维护性、团队协作效率及系统扩展能力。 一、模块化设计的核心…

    日期数据渲染转换问题

    今天在学习Springboot框架时&#xff0c;想做一个非常简单的增删改查巩固一下&#xff0c;结果在数据渲染上出现了一个小问题&#xff0c;如图数据库中的数据一切正常 但是在前端渲染时&#xff0c;是下面这个效果 这是因为数据库存储的日期类型数据在前端渲染时&#xff0c;没…

    ubuntu18.04编译qt5.14.2源码

    ubuntu18.04编译qt5.14.2源码 文章目录 ubuntu18.04编译qt5.14.2源码[toc]1 前言2 参考文档3 下载源码3.1 方法13.2 方法23.3 方法3 4 ubuntu编译qt源码4.1 环境准备4.2 设置交换分区大小4.3 编译源码4.4 添加环境变量4.5 验证编译结果4.6 编译帮助文档&#xff08;qch&#xf…

    创建指定版本的vite项目

    1、获取vite的版本号 npm view create-vite versions 注:4.4.1版本即对应着node16版本的项目 2、创建制定版本的vite项目 npm init vite<version>

    iOS 初识RunLoop

    iOS 初识RunLoop 文章目录 iOS 初识RunLoopRunLoop的概念RunLoop的功能RunLoop和线程的关系RunLoop的结构ModeObserverTimer 和 source小结 RunLoop的核心RunLoop的流程RunLoop的应用AutoreleasePool响应触控事件刷新界面常驻线程网络请求NSTimer 和 CADisplayLinkNSTimerGCDTi…

    电子电路仿真实验教学平台重磅上线!——深圳航天科技创新研究院倾力打造,助力高校教学数字化转型

    在传统电子电路课堂中&#xff0c;实验室的灯光总与高昂的成本、拥挤的设备、反复的耗材损耗相伴&#xff0c;而教师不得不面对这样的现实&#xff1a;有限的硬件资源束缚着教学深度&#xff0c;不可逆的实验风险制约着创新探索&#xff0c;固化的时空场景阻碍着个性化学习。当…

    搭建一个WordPress网站需要多少成本

    WordPress 最初可能只是一个简单的博客平台。但近年来&#xff0c;它不仅成为了最好的博客平台&#xff0c;还成为了一个全面的内容管理系统。白宫、jQuery、NGINX、《纽约时报》等企业都把 WordPress 作为自己的网上家园。 不过&#xff0c;它们只是其中的佼佼者。根据 Built…