首页
/ 在 Next.js 中实现页面切换动画:基于 next-page-transitions 的完整实践指南

在 Next.js 中实现页面切换动画:基于 next-page-transitions 的完整实践指南

2026-09-07 23:18:06作者:魏侃纯Zoe

页面跳转时生硬的白屏与瞬时切换常常破坏应用的使用体验。本指南以 Next.js 官方仓库中的 with-next-page-transitions 示例 为依托,系统讲解如何借助 next-page-transitions 组件为 App Router 之外的 Pages Router 应用接入优雅的页面过渡动画与"加载中"状态。读完你将掌握该库的核心 API(PageTransitionpageTransitionReadyToEnter)、CSS class 命名约定、加载指示器的时序配置,以及如何在自定义 _app.js 中将它接入 Next.js。

示例背后的核心思路:应用级组件 + CSS 驱动动画

next-page-transitions 是一个挂在应用顶层(即 Next.js 的 _app.js)的组件,职责是接管页面切换时的挂载与卸载时机。它与 react-transition-group 的思路类似:不是替你去写动画,而是在页面容器上按状态依次追加约定好的 CSS class(如 -enter-enter-active-exit-exit-active),真正的过渡效果由你自己的 CSS 定义。

它在 Next.js 场景下有两个显著收益:

  • 同一时刻只挂载一个页面,避免旧页面与新页面短暂并存导致的布局抖动;
  • 替应用管理动画时序,让"退出旧页 → 进入新页"的时长由统一的 timeout 控制。

该特性尤其适合具有共享布局元素(例如导航栏 Navbar)的应用——共享壳保持不动,只有内容区做平滑过渡,这与 Pages Router 下所有页面共享同一个 _app 的应用形态天然契合。

如何启动这个示例

方式一:使用 create-next-app 一键初始化

官方示例清单中的项目均可通过 create-next-app 直接引导。create-next-app 提供 -e, --example [name]|[github-url] 参数指定示例名,也支持用 --example-path <path-to-example> 定位示例中的子目录(见 packages/create-next-app/README.md)。

npx create-next-app --example with-next-page-transitions with-next-page-transitions-app
yarn create next-app --example with-next-page-transitions with-next-page-transitions-app
pnpm create next-app --example with-next-page-transitions with-next-page-transitions-app

以上三条命令等价,仅包管理器不同;执行后会在当前目录生成名为 with-next-page-transitions-app 的项目。

方式二:直接阅读/复用仓库中的示例目录

也可以在仓库中直接浏览 examples/with-next-page-transitions,目录结构如下:

examples/with-next-page-transitions/
├── components/
│   └── Loader.js        # 页面加载期间显示的转圈组件
├── pages/
│   ├── _app.js          # 全局入口,挂载 PageTransition 并定义动画 CSS
│   ├── _document.js     # 自定义 Document,注入 Bootstrap 样式
│   ├── index.js         # 首页(蓝底 "Hello, world!")
│   └── about.js         # About 页(模拟延迟加载,演示 loading 态)
├── package.json
└── README.md

示例的 package.json 声明了三个标准脚本与依赖,next-page-transitions 的版本被固定为 1.0.0-beta.2

"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
},
"dependencies": {
  "next": "latest",
  "next-page-transitions": "1.0.0-beta.2",
  "react": "^18.2.0",
  "react-dom": "^18.2.0"
}

初始化后依次执行 npm run dev(开发)或 npm run build && npm run start(生产)即可看到两个页面:首页点击 "About us" 跳转 About 页时,会出现约 2 秒的加载动画后再淡入新页面。

核心实现一:在 _app.js 中接入 PageTransition

Pages Router 中每次路由变化都会重新渲染根组件,因此 PageTransition 要包在 _app.js 返回树的页面组件外层。完整实现见 pages/_app.js,其配置如下:

import Head from "next/head";
import { PageTransition } from "next-page-transitions";
import Loader from "../components/Loader";

const TIMEOUT = 400;

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
      </Head>
      <PageTransition
        timeout={TIMEOUT}
        classNames="page-transition"
        loadingComponent={<Loader />}
        loadingDelay={500}
        loadingTimeout={{
          enter: TIMEOUT,
          exit: 0,
        }}
        loadingClassNames="loading-indicator"
      >
        <Component {...pageProps} />
      </PageTransition>
    </>
  );
}

各 prop 的含义如下表:

Prop 示例取值 作用
timeout 400 单个过渡阶段(enter 或 exit)的时长(ms),必须与 CSS 里 transition 的时长保持一致
classNames "page-transition" CSS class 前缀,库会拼出 page-transition-enterpage-transition-enter-activepage-transition-exitpage-transition-exit-active
loadingComponent <Loader /> 目标页面尚未就绪时渲染的加载 UI
loadingDelay 500 触发 loading 态的延迟(ms),避免快速切换时加载条一闪而过
loadingTimeout { enter: 400, exit: 0 } loading 容器自身的过渡时长;exit: 0 表示页面就绪后立即隐藏加载条
loadingClassNames "loading-indicator" loading 态容器的 CSS 前缀(拼出 loading-indicator-appear 等 class)

若页面在 loadingDelay(500ms)内就绪(快速导航),加载组件根本不会出现,导航依然平滑——这正是"快则无感、慢则有反馈"体验的关键。

配套的过渡 CSS

同文件的 <style jsx global> 定义了全部动画 class。jsx global 确保样式穿透到 PageTransition 渲染出的容器上:

.page-transition-enter {
  opacity: 0;
  transform: translate3d(0, 20px, 0);   /* 新页从下方 20px、透明开始 */
}
.page-transition-enter-active {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  transition: opacity 400ms, transform 400ms;   /* 数值与 TIMEOUT=400 对齐 */
}
.page-transition-exit {
  opacity: 1;
}
.page-transition-exit-active {
  opacity: 0;
  transition: opacity 400ms;   /* 旧页只做淡出 */
}
.loading-indicator-appear,
.loading-indicator-enter {
  opacity: 0;
}
.loading-indicator-appear-active,
.loading-indicator-enter-active {
  opacity: 1;
  transition: opacity 400ms;   /* 加载指示器淡入 */
}

时序编排逻辑:路由变化时旧页先执行 -exit/-exit-active 淡出并卸载 → 若新页尚未就绪则渲染 loading 指示器 → 新页就绪后执行 -enter/-enter-active 淡入。因此 _app.js 里定义的全局 CSS 就是整个过渡"剧本",颜色、位移、时长都可自由定制。

核心实现二:页面的"延迟就绪"协议

默认情况下 PageTransition 会立即执行新页进入动画。示例的 About 页则演示了手动延迟进入,让导航先呈现 loading 态再播放进入动画。协议由两个要素构成,见 pages/about.js

1. 静态标记 About.pageTransitionDelayEnter = true

在组件上声明该静态字段,告知 PageTransition:"本页面需要手动调用就绪回调后才允许进入",由此禁用默认的立即进入:

About.pageTransitionDelayEnter = true;

2. prop pageTransitionReadyToEnter

设置了延迟进入后,PageTransition 会向页面注入 pageTransitionReadyToEnter 回调。页面在数据/资源真正就绪后调用它,动画才会继续:

const About = (props) => {
  const [loaded, setLoaded] = useState(false);
  const { pageTransitionReadyToEnter } = props;

  useEffect(() => {
    const timeoutId = setTimeout(() => {
      pageTransitionReadyToEnter();   // 通知外层:内容已就绪,可以进入
      setLoaded(true);
    }, 2000);                          // 示例中用 2s 定时器模拟数据加载
    return () => {
      clearTimeout(timeoutId);         // 卸载时清理定时器
    };
  }, [pageTransitionReadyToEnter]);

  if (!loaded) return null;            // 未就绪时不渲染内容,交给 loading 组件占位

  return ( /* ...页面内容... */ );
};

About.propTypes = {
  pageTransitionReadyToEnter: PropTypes.func,
};
About.defaultProps = {
  pageTransitionReadyToEnter: () => {},   // 兜底空实现,防止意外为 undefined
};

这里的要点在于把 pageTransitionReadyToEnter() 放进 useEffect 的异步回调中——真实项目中,它会出现在 fetch 数据、Image 解码或自定义 hook 的 .then() 里,即"内容真正渲染所需依赖全部就绪"的那一刻。同时通过 PropTypes 声明其类型、用 defaultProps 提供空实现兜底,避免任何一处漏传导致运行时错误。

加载指示器组件 Loader

当目标页面声明延迟进入且尚未调用就绪回调时,_app.js 中配置的 loadingComponent 便会登场。示例的加载动画是一个纯 CSS 旋转圆环,见 components/Loader.js

const Loader = () => (
  <div className="loader">
    <style jsx>{`
      .loader {
        border: 8px solid #f3f3f3;              /* 浅灰底环 */
        border-top: 8px solid #3498db;          /* 蓝色顶环,制造缺口 */
        border-radius: 50%;
        width: 40px;
        height: 40px;
        animation: spin 2s linear infinite;     /* 匀速无限旋转 */
        margin-left: auto;
        margin-right: auto;
        margin-top: 40px;
      }
      @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
      }
    `}</style>
  </div>
);

它由 loadingTimeout.enter(淡入,400ms)与 loadingClassNames="loading-indicator"(淡入透明度过渡)配合,在导航发起后优雅地显现。

页面与全局样式的组织方式

两个演示页面均使用 Bootstrap 色彩类做视觉区分,便于观察过渡效果,且都通过 Next.js 内置的 Link 进行客户端导航——只有客户端路由跳转才会触发过渡动画,整页刷新不会:

  • pages/index.js:蓝色主色(bg-primary)的首页,含 "Hello, world!" 标题与指向 /about 的链接;
  • pages/about.js:绿色主色(bg-success)的 About 页,文案引导用户观察加载动画,并含返回首页的链接。

pages/_document.js 通过 <Head> 引入了 Bootstrap 4 CDN 样式表(含 SRI 完整性校验与 crossOrigin="anonymous"),并追加了 .page { height: 100vh; } 的全局规则,让每个页面充满整屏,切换时不会因内容高度差异产生跳动感。页面自身不重复引入,样式保持单一来源。

关于运行时依赖的提醒:示例依赖的 next-page-transitions1.0.0-beta.2)为较早期的社区库,示例本身基于 Pages Router 与 React 18 编写;若要复用到当前代码库形态(例如 App Router),需自行验证该库与路由模型、React 版本的兼容性。

小结:一套可迁移的页面过渡模式

回顾示例,接入 next-page-transitions 只需三步:

  1. 接入:在 _app.jsPageTransition 包裹 <Component />,配置 timeoutclassNames
  2. 写动画:在全局样式中定义前缀对应的 -enter/-enter-active/-exit/-exit-active 四类 CSS 规则,时长与 timeout 一致;
  3. 可选加 loading:为"慢页面"声明 pageTransitionDelayEnter = true,数据就绪后调用注入的 pageTransitionReadyToEnter(),并配置 loadingComponentloadingDelayloadingTimeoutloadingClassNames

这套"CSS class 状态机 + 就绪回调"的模式与是否使用 next-page-transitions 无关,即使日后替换为其他动画方案,在 Next.js 中"切换旧页 → 展示 loading → 注入就绪回调 → 淡入新页"的编排思路也依然成立。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
594
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388