如何在Next.js中结合layout.tsx与_app.tsx实现路由及页面过渡
在Next.js App Router中整合Framer-Motion页面过渡与RootLayout
核心逻辑调整
App Router模式下,旧的_app.tsx不再生效,需将页面过渡逻辑迁移到App Router的结构中。核心是把AnimatePresence放到客户端组件内——因为RootLayout默认是服务器组件,无法直接调用Framer-Motion的客户端API。
具体实现步骤
1. 移除旧的_app.tsx
App Router不需要这个文件,直接删除即可。
2. 新建客户端过渡包裹组件
创建app/components/PageTransitionWrapper.tsx(路径可自定义),负责提供AnimatePresence运行环境:
"use client"; import { AnimatePresence } from "framer-motion"; import { usePathname } from "next/navigation"; export default function PageTransitionWrapper({ children, }: { children: React.ReactNode; }) { const pathname = usePathname(); return ( <AnimatePresence initial={false} mode="popLayout"> {React.cloneElement(children as React.ReactElement, { key: pathname })} </AnimatePresence> ); }
3. 修改RootLayout集成过渡组件
在根布局中引入客户端过渡组件,包裹页面内容:
import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; import Header from "@/components/layout/header"; import Lines from "@/components/line/lines"; import Footer from "@/components/layout/footer"; import PageTransitionWrapper from "./components/PageTransitionWrapper"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "A website for gamers", description: "A website for gamers is a website for gamers.", }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body className={inter.className}> <main className="min-h-screen grid grid-rows-[auto,1fr,auto]"> <Header /> <div className="relative overflow-hidden"> <Lines /> {/* 用过渡组件包裹页面内容 */} <PageTransitionWrapper>{children}</PageTransitionWrapper> </div> <Footer /> </main> </body> </html> ); }
4. 调整页面组件(以Page.tsx为例)
确保页面接收路由key并传递给过渡组件:
"use client"; import Hero from "@/components/hero/hero"; import PageTransition from "@/components/pageTransition"; export default function IndexPage({ key }: { key: string }) { return ( <PageTransition key={key}> <Hero /> </PageTransition> ); }
5. 简化PageTransition组件(可选)
如果不需要外部引用组件DOM,可去掉forwardRef简化代码:
"use client"; import { motion } from "framer-motion"; export default function PageTransition({ children, ...rest }: { children: React.ReactNode; [key: string]: any; }) { const fromTheBottom = { y: "100%" }; const inTheMiddle = { y: 0 }; const toTheTop = { y: "-100%" }; const transition = { duration: 0.6, ease: "easeInOut" }; return ( <motion.section className="relative flex-1 h-full flex flex-col" initial={fromTheBottom} animate={inTheMiddle} exit={toTheTop} transition={transition} {...rest} > {children} </motion.section> ); }
关键注意点
- 用
usePathname替代旧的useRouter().asPath,获取当前路由作为页面唯一标识key - 所有使用Framer-Motion的组件必须加上
"use client"指令,否则无法在服务器组件中运行 AnimatePresence必须直接包裹带key的动态元素,才能检测页面切换并触发过渡动画
内容的提问来源于stack exchange,提问作者Benjamin
相关产品推荐
相关产品推荐

