You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js _app.js中动态前置加载脚本问题排查及执行顺序确认

问题分析与解决方案

一、子域'X'下脚本仍加载的核心原因

你的代码逻辑存在一个关键错误:beforeInteractive 策略的脚本是在服务器端渲染/构建阶段就被注入到HTML中的,而你通过useEffect修改load状态的操作是在客户端浏览器运行时才触发的。初始load值为true,导致服务器端直接把OneTrust脚本输出到HTML里,等客户端useEffect执行并将load设为false时,脚本已经被浏览器加载完成,根本无法阻止。

二、正确的域名判断方案

要实现子域'X'下不加载脚本,必须在脚本被注入到HTML之前完成域名判断,不能依赖客户端的状态更新。以下是两种可行方案:

方案1:客户端直接判断(适用于静态生成/ISR场景)

放弃useState和useEffect,直接在渲染时通过typeof window判断环境并检查域名:

function MyApp({ Component, pageProps }) {
  // 仅在客户端环境下,且域名不包含'X'时加载脚本
  const shouldLoadOneTrust = typeof window !== 'undefined' && !window.location.host.includes('X');

  return (
    <Fragment>
      <Head></Head>
      {shouldLoadOneTrust ? (
        <>
          <Script
            strategy="beforeInteractive"
            src="src"
            type="text/javascript"
            charSet="UTF-8"
            data-domain-script="some-id"
          ></Script>
          <Script
            id="test"
            strategy="beforeInteractive"
            type="text/javascript"
            dangerouslySetInnerHTML={{
              __html: `
                // 你的OneTrust自定义JS代码
              `,
            }}
          />
        </>
      ) : null}

      {/* 其他业务脚本 */}
      <Component {...pageProps} />
    </Fragment>
  );
}

这种方式在服务器端渲染时不会输出脚本(因为typeof window在Node环境下为undefined),客户端Hydrate时会直接判断域名并决定是否加载,完全符合需求。

方案2:服务器端+客户端双判断(适用于SSR/SSG全场景)

如果需要在服务器端就完成域名判断(比如避免不必要的脚本传输),可以在_app.js中使用getInitialProps获取请求头中的域名:

function MyApp({ Component, pageProps, shouldLoadOneTrust }) {
  return (
    <Fragment>
      <Head></Head>
      {shouldLoadOneTrust ? (
        <>
          <Script
            strategy="beforeInteractive"
            src="src"
            type="text/javascript"
            charSet="UTF-8"
            data-domain-script="some-id"
          ></Script>
          <Script
            id="test"
            strategy="beforeInteractive"
            type="text/javascript"
            dangerouslySetInnerHTML={{
              __html: `
                // 你的OneTrust自定义JS代码
              `,
            }}
          />
        </>
      ) : null}

      {/* 其他业务脚本 */}
      <Component {...pageProps} />
    </Fragment>
  );
}

MyApp.getInitialProps = async (context) => {
  let shouldLoadOneTrust = true;
  // 服务器端:从请求头获取域名判断
  if (context.req) {
    const host = context.req.headers.host;
    if (host.includes('X')) {
      shouldLoadOneTrust = false;
    }
  } else {
    // 客户端:直接读取当前域名
    shouldLoadOneTrust = !window.location.host.includes('X');
  }
  return {
    pageProps: {},
    shouldLoadOneTrust,
  };
};

这种方式覆盖了服务器端渲染和客户端导航的所有场景,能彻底避免子域'X'下加载OneTrust脚本。

三、关于脚本执行顺序的问题

你当前的方式无法保证OneTrust脚本先于其他脚本执行,原因是:

  1. 初始load为true导致服务器端输出脚本,但客户端useEffect的状态修改是滞后的,脚本加载时机不受控制;
  2. 若其他脚本使用了相同的beforeInteractive策略,执行顺序由代码中的位置决定,但你的条件判断逻辑错误,导致不该加载的场景也加载了脚本。

正确的顺序保证方式

只要满足以下两个条件,就能确保OneTrust脚本先于其他脚本执行:

  1. 将OneTrust脚本的strategy设为beforeInteractive(Next.js中优先级最高的脚本加载策略,会在HTML解析完成前执行);
  2. 将OneTrust脚本的代码位置放在所有其他脚本的前面。

结合前面的正确判断方案,就能同时实现"子域'X'不加载"和"优先执行"两个需求。

内容的提问来源于stack exchange,提问作者Tapu Das

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 02:30:52