如何加载和优化脚本
布局脚本
要为多个路由加载第三方脚本,导入 next/script
并直接在布局组件中包含脚本:
- TypeScript
- JavaScript
import Script from 'next/script'
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<>
<section>{children}</section>
<Script src="https://example.com/script.js" />
</>
)
}
import Script from 'next/script'
export default function DashboardLayout({ children }) {
return (
<>
<section>{children}</section>
<Script src="https://example.com/script.js" />
</>
)
}
当用户访问文件夹路由(例如 dashboard/page.js
)或任何嵌套路由(例如 dashboard/settings/page.js
)时,第三方脚本会被获取。Next.js 将确保脚本只加载一次,即使用户在同一布局中的多个路由之间导航。
应用程序脚本
要为所有路由加载第三方脚本,导入 next/script
并直接在根布局中包含脚本:
- TypeScript
- JavaScript
import Script from 'next/script'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
<Script src="https://example.com/script.js" />
</html>
)
}
import Script from 'next/script'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
<Script src="https://example.com/script.js" />
</html>
)
}
要为所有路由加载第三方脚本,导入 next/script
并直接在自定义 _app
中包含脚本:
import Script from 'next/script'
export default function MyApp({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
<Script src="https://example.com/script.js" />
</>
)
}
当访问应用程序中的 任何 路由时,此脚本将加载并执行。Next.js 将确保脚本只加载一次,即使用户在多个页面之间导航。
建议:我们建议只在特定页面或布局中包含第三方脚本,以最小化对性能的任何不必要影响。
策略
虽然 next/script
的默认行为允许你在任何页面或布局中加载第三方脚本,但你可以通过使用 strategy
属性来微调其加载行为:
beforeInteractive
:在任何 Next.js 代码之前和任何页面水合发生之前加载脚本。afterInteractive
:(默认)在页面发生一些水合后早期加载脚本。lazyOnload
:在浏览器空闲时间稍后加载脚本。worker
:(实验性)在 Web Worker 中加载脚本。
参考 next/script
API 参考文档以了解每个策略及其用例的更多信息。
将脚本卸载到 Web Worker(实验性)
警告:
worker
策略尚未稳定,还不能与 App Router 一起工作。谨慎使用。
使用 worker
策略的脚本被卸载并在 Web Worker 中使用 Partytown 执行。这可以通过将主线程专用于应用程序代码的其余部分来提高站点的性能。
此策略仍然是实验性的,只有在 next.config.js
中启用 nextScriptWorkers
标志时才能使用:
module.exports = {
experimental: {
nextScriptWorkers: true,
},
}
然后,运行 next
(通常是 npm run dev
或 yarn dev
),Next.js 将指导你安装所需的包以完 成设置:
npm run dev
你会看到类似这样的说明:请通过运行 npm install @builder.io/partytown
安装 Partytown
设置完成后,定义 strategy="worker"
将自动在你的应用程序中实例化 Partytown 并将脚本卸载到 Web Worker。
- TypeScript
- JavaScript
import Script from 'next/script'
export default function Home() {
return (
<>
<Script src="https://example.com/script.js" strategy="worker" />
</>
)
}
import Script from 'next/script'
export default function Home() {
return (
<>
<Script src="https://example.com/script.js" strategy="worker" />
</>
)
}
在 Web Worker 中加载第三方脚本时需要考虑许多权衡。请参阅 Partytown 的权衡文档以获取更多信息。
使用自定义 Partytown 配置
虽然 worker
策略不需要任何额外配置即可工作,但 Partytown 支持使用配置对象来修改其某些设置,包括启用 debug
模式和转发事件和触发器。
如果你想添加其他配置选项,可以将其包含在 自定义 _document.js
中使用的 <Head />
组件内:
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head>
<script
data-partytown-config
dangerouslySetInnerHTML={{
__html: `
partytown = {
lib: "/_next/static/~partytown/",
debug: true
};
`,
}}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
为了修改 Partytown 的配置,必须满足以下条件:
- 必须使用
data-partytown-config
属性以覆盖 Next.js 使用的默认配置 - 除非你决定将 Partytown 的库文件保存在单独的目录中,否则配置对象中必须包含
lib: "/_next/static/~partytown/"
属性和值,以便让 Partytown 知道 Next.js 存储必要静态文件的位置。
注意:如果你使用 资产前缀 并希望修改 Partytown 的默认配置,你必须将其作为
lib
路径的一部分包含。
查看 Partytown 的配置选项以查看可以添加的其他属性的完整列表。
内联脚本
内联脚本或不是从外部文件加载的脚本也由 Script 组件支持。可以通过将 JavaScript 放在大括号内来编写:
<Script id="show-banner">
{`document.getElementById('banner').classList.remove('hidden')`}
</Script>
或通过使用 dangerouslySetInnerHTML
属性:
<Script
id="show-banner"
dangerouslySetInnerHTML={{
__html: `document.getElementById('banner').classList.remove('hidden')`,
}}
/>
警告:必须为内联脚本分配
id
属性,以 便 Next.js 跟踪和优化脚本。
执行额外代码
事件处理程序可以与 Script 组件一起使用,以在发生某些事件后执行额外代码:
onLoad
:在脚本完成加载后执行代码。onReady
:在脚本完成加载后以及每次组件挂载时执行代码。onError
:如果脚本加载失败,执行代码。
这些处理程序只有在 next/script
被导入并在定义了 "use client"
作为代码第一行的客户端组件内部使用时才会工作:
- TypeScript
- JavaScript
'use client'
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
onLoad={() => {
console.log('Script has loaded')
}}
/>
</>
)
}
'use client'
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
onLoad={() => {
console.log('Script has loaded')
}}
/>
</>
)
}
参考 next/script
API 参考以了解每个事件处理程序的更多信息并查看示例。
这些处理程序只有在 next/script
被导入并在定义了 "use client"
作为代码第一行的客户端组件内部使用时才会工作:
- TypeScript
- JavaScript
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
onLoad={() => {
console.log('Script has loaded')
}}
/>
</>
)
}
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
onLoad={() => {
console.log('Script has loaded')
}}
/>
</>
)
}
参考 next/script
API 参考以了解每个事件处理程序的更多信息并查看示例。
额外属性
有许多可以分配给 <script>
元素的 DOM 属性不被 Script 组件使用,如 nonce
或自定义数据属性。包含任何额外属性将自动转发到包含在 HTML 中的最终优化 <script>
元素。
- TypeScript
- JavaScript
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
id="example-script"
nonce="XUENAJFW"
data-test="script"
/>
</>
)
}
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
id="example-script"
nonce="XUENAJFW"
data-test="script"
/>
</>
)
}
- TypeScript
- JavaScript
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
id="example-script"
nonce="XUENAJFW"
data-test="script"
/>
</>
)
}
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
id="example-script"
nonce="XUENAJFW"
data-test="script"
/>
</>
)
}