Next.js 13客户端组件实现锚点跳转及服务端数据获取的问题
解决方案
核心思路
把交互逻辑(锚点跳转)和数据获取逻辑拆分为独立组件:交互部分用客户端组件(需'use client'指令),数据获取部分用服务端组件(默认就是,无需特殊指令),再通过根页面(服务端组件)整合两者——因为服务端组件可以渲染客户端组件,完美适配你的需求。
步骤1:将头部菜单改为客户端组件
头部需要处理点击事件和DOM滚动,必须作为客户端组件。添加'use client'指令后,即可安全操作DOM实现平滑跳转:
'use client'; export default function Header() { const scrollToSection = (sectionId) => { const targetElement = document.getElementById(sectionId); if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth' }); } }; return ( <header className="header"> <nav> <button onClick={() => scrollToSection('about-section')}>关于我们</button> {/* 其他菜单项,比如首页、联系我们等 */} </nav> </header> ); }
步骤2:将About组件改为服务端组件
About需要用Prisma读取数据库,必须在服务端运行。直接写async/await逻辑即可,无需'use client':
首先确保PrismaClient以单例模式创建(避免重复实例化),在prisma/client.js中:
import { PrismaClient } from '@prisma/client'; const prisma = global.prisma || new PrismaClient(); if (process.env.NODE_ENV !== 'production') global.prisma = prisma; export default prisma;
然后编写About组件:
import prisma from './prisma/client'; export default async function About() { // 直接在服务端从数据库拉取数据 const aboutData = await prisma.about.findFirst(); return ( <section id="about-section" className="about-section"> <h2>关于我们</h2> {aboutData ? ( <p>{aboutData.content}</p> ) : ( <p>暂无关于我们的内容</p> )} </section> ); }
步骤3:在根页面整合组件
App Router中的page.js默认是服务端组件,可以直接导入并渲染客户端的Header和服务端的About:
import Header from './Header'; import About from './About'; import Home from './Home'; // 你的首页区块组件(可按需设为客户端/服务端) import Contact from './Contact'; // 联系我们区块组件 export default async function LandingPage() { // 如果有其他服务端数据需求,也可在这里统一处理 return ( <div className="landing-page"> <Header /> <Home /> <About /> <Contact /> </div> ); }
为什么这样可行?
- 服务端组件(About、page.js)运行在服务器,不会暴露Prisma代码到浏览器,解决了PrismaClient无法在浏览器运行的问题。
- 客户端组件(Header)运行在浏览器,负责处理交互逻辑,避开了客户端组件不能用
async/await的限制。 - 服务端组件允许渲染客户端组件,完全符合Next.js 13 App Router的组件嵌套规则。
内容的提问来源于stack exchange,提问作者Angga Ari Wijaya
相关产品推荐
相关产品推荐

