Script 脚本

2026年9月6日

第三方统计、地图、偶尔要嵌的旧库,用 next/script 比手写 <script> 好管:去重、按策略插入、跟路由生命周期对齐。


局部引入

只在某条路由需要时,写在该 page.tsx

import Script from "next/script";

export default function Page() {
  return (
    <div>
      <Script src="https://example.com/sdk.js" />
    </div>
  );
}
tsx

切到这条路由才加载,之后走缓存。底层仍是往文档里插 <script>


全局引入

放进根 layout.tsx,全站一份:

import Script from "next/script";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="zh-CN">
      <body>
        {children}
        <Script src="https://example.com/sdk.js" strategy="afterInteractive" />
      </body>
    </html>
  );
}
tsx

不必塞进 <head>,组件自己会找合适位置。


strategy

时机备注
beforeInteractive页面可交互之前会挡住渲染,只给真正关键的脚本
afterInteractive水合之后(默认)统计、一般 SDK
lazyOnload浏览器空闲聊天挂件、非关键
worker实验性不稳定,先别用

给脚本一个稳定 id,方便框架去重和排错:

<Script id="analytics" strategy="afterInteractive" src="https://example.com/a.js" />
tsx

内联

没有外链时,用子节点或 dangerouslySetInnerHTML必须带 id

<Script id="boot" strategy="afterInteractive">
  {`window.__boot = true`}
</Script>
tsx
<Script
  id="boot-2"
  strategy="afterInteractive"
  dangerouslySetInnerHTML={{
    __html: `window.__boot = true`,
  }}
/>
tsx

能外链就外链,内联不好缓存,也更容易和 CSP 打架。


事件

onLoadonReadyonError 要在客户端组件里才靠谱:

"use client";

import Script from "next/script";

export default function Map() {
  return (
    <Script
      src="https://example.com/map.js"
      onLoad={() => console.log("loaded")}
      onReady={() => console.log("ready or remounted")}
      onError={() => console.error("failed")}
    />
  );
}
tsx
  • onLoad:脚本第一次加载成功
  • onReady:加载完成,以及组件以后每次挂载
  • onError:失败

能 npm 装进仓库的库,优先 import,不要用 Script 从 CDN 拉 React 生态包。Script 留给「只能给一段外链」的第三方。

下一篇开始静态导出:纯 HTML 怎么打出来,以及哪些能力会丢掉。


参考文档