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脚本先于其他脚本执行,原因是:
- 初始
load为true导致服务器端输出脚本,但客户端useEffect的状态修改是滞后的,脚本加载时机不受控制; - 若其他脚本使用了相同的
beforeInteractive策略,执行顺序由代码中的位置决定,但你的条件判断逻辑错误,导致不该加载的场景也加载了脚本。
正确的顺序保证方式
只要满足以下两个条件,就能确保OneTrust脚本先于其他脚本执行:
- 将OneTrust脚本的
strategy设为beforeInteractive(Next.js中优先级最高的脚本加载策略,会在HTML解析完成前执行); - 将OneTrust脚本的代码位置放在所有其他脚本的前面。
结合前面的正确判断方案,就能同时实现"子域'X'不加载"和"优先执行"两个需求。
内容的提问来源于stack exchange,提问作者Tapu Das
相关产品推荐
相关产品推荐

