You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 16:03:46