Next.js src/app目录结构下如何在head标签中添加脚本?
在Next.js App Router(src/app结构)中为特定页面添加内容的方法
1. 使用Metadata API(官方推荐)
App Router下官方主推用Metadata API管理标签,支持静态定义和动态生成,针对特定页面,直接在对应页面的page.tsx/page.jsx文件里导出配置即可。
静态Metadata配置
如果不需要动态数据,直接导出metadata对象就能添加脚本、meta标签、样式链接等:
// src/app/about/page.tsx import type { Metadata } from 'next'; export const metadata: Metadata = { title: '关于我们', description: '这是站点的关于页面', // 自定义meta标签 other: { 'custom-tag': '自定义内容', }, // 添加外部脚本 scripts: [ // 外部脚本,默认注入到head { src: 'https://example.com/script.js', strategy: 'beforeInteractive', // 可选加载策略:beforeInteractive/afterInteractive/lazyOnload }, // 内联脚本 { dangerouslySetInnerHTML: { __html: `console.log('页面加载时执行的内联脚本')`, }, }, ], // 添加外部样式 links: [ { rel: 'stylesheet', href: 'https://example.com/custom-style.css', }, ], }; export default function AboutPage() { return <div>关于页面内容</div>; }
动态Metadata配置
如果需要根据页面参数(比如文章slug)动态生成内容,用generateMetadata函数:
// src/app/post/[slug]/page.tsx import type { Metadata, ResolvingMetadata } from 'next'; type Props = { params: { slug: string }; }; export async function generateMetadata( { params }: Props, parent: ResolvingMetadata ): Promise<Metadata> { // 根据slug获取文章数据 const post = await fetch(`https://api.example.com/posts/${params.slug}`).then(res => res.json()); // 可以继承父布局的metadata const parentImages = (await parent).openGraph?.images || []; return { title: post.title, description: post.excerpt, scripts: [ { src: `/scripts/post-${params.slug}.js`, strategy: 'afterInteractive', }, ], openGraph: { images: [...parentImages, post.coverUrl], }, }; } export default function PostPage({ params }: Props) { return <div>文章内容:{params.slug}</div>; }
2. 使用Next.js Script组件
如果需要更灵活的脚本加载控制,用next/script组件,它会自动把脚本注入到或,根据设置的strategy调整时机:
// src/app/contact/page.tsx import Script from 'next/script'; export default function ContactPage() { return ( <div> <h1>联系我们</h1> {/* 注入到head的脚本,beforeInteractive策略会在页面交互前加载 */} <Script src="https://example.com/contact-form-script.js" strategy="beforeInteractive" id="contact-script" /> {/* 内联脚本,注入到head */} <Script id="inline-contact-handler" strategy="afterInteractive" > {` document.getElementById('contact-form').addEventListener('submit', (e) => { e.preventDefault(); console.log('表单提交逻辑触发'); }); `} </Script> <form id="contact-form"> {/* 表单内容 */} </form> </div> ); }
加载策略说明:
beforeInteractive:适合核心脚本,在页面HTML发送到客户端前注入到headafterInteractive:页面可交互后立即加载,适合非核心但需快速执行的脚本lazyOnload:浏览器空闲时加载,适合非紧急脚本
3. 兼容方式:直接使用next/head
虽然官方推荐Metadata API,但也可以用next/head组件快速实现,不过需要标记为客户端组件(加'use client'指令):
// src/app/faq/page.tsx 'use client'; import Head from 'next/head'; export default function FaqPage() { return ( <> <Head> <title>常见问题</title> <meta name="keywords" content="FAQ,帮助中心,问题解答" /> <script src="https://example.com/faq-script.js" async></script> <link rel="preconnect" href="https://fonts.googleapis.com" /> </Head> <div>常见问题内容</div> </> ); }
注意:这种方式适合旧代码迁移,但Metadata API能更好地适配App Router的静态生成、服务器组件等特性。
内容的提问来源于stack exchange,提问作者Oleh
相关产品推荐
相关产品推荐

