---
title: "过渡效果"
description: "使用 Vue 或原生浏览器的视图过渡（View Transitions）在页面和布局之间应用过渡效果。"
canonical_url: "https://nuxt.zhcndoc.com/docs/3.x/getting-started/transitions"
---
# 过渡效果

> 使用 Vue 或原生浏览器的视图过渡（View Transitions）在页面和布局之间应用过渡效果。

::note
Nuxt 利用 Vue 的 [`<Transition>`](https://vue.zhcndoc.com/guide/built-ins/transition.html#the-transition-component) 组件在页面和布局之间应用过渡效果。
::

## 页面过渡

你可以启用页面过渡，为所有[页面](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/pages)应用自动过渡效果。

```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
  app: {
    pageTransition: { name: 'page', mode: 'out-in' },
  },
})
```

::note
如果你同时更改布局和页面，则此处设置的页面过渡不会运行。此时，你应该设置[布局过渡](https://nuxt.zhcndoc.com/docs/3.x/getting-started/transitions#layout-transitions)。
::

要开始为页面之间添加过渡，请将以下 CSS 添加到你的 [`app.vue`](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/app) 中：

::code-group
```vue [app.vue]
<template>
  <NuxtPage />
</template>

<style>
.page-enter-active,
.page-leave-active {
  transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
  opacity: 0;
  filter: blur(1rem);
}
</style>
```

```vue [pages/index.vue]
<template>
  <div>
    <h1>首页</h1>
    <NuxtLink to="/about">关于页面</NuxtLink>
  </div>
</template>
```

```vue [pages/about.vue]
<template>
  <div>
    <h1>关于页面</h1>
    <NuxtLink to="/">首页</NuxtLink>
  </div>
</template>
```
::

下面是在页面导航时产生的效果：

<video controls class="rounded" poster="https://res.cloudinary.com/nuxt/video/upload/v1665061349/nuxt3/nuxt3-page-transitions_umwvmh.jpg">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1665061349/nuxt3/nuxt3-page-transitions_umwvmh.mp4" type="video/mp4">
</video>

要为页面设置不同的过渡效果，请在该页面的 [`definePageMeta`](https://nuxt.zhcndoc.com/docs/3.x/api/utils/define-page-meta) 中设置 `pageTransition` 键：

::code-group
```vue twoslash [pages/about.vue]
<script setup lang="ts">
definePageMeta({
  pageTransition: {
    name: 'rotate',
  },
})
</script>
```

```vue [app.vue]
<template>
  <NuxtPage />
</template>

<style>
/* ... */
.rotate-enter-active,
.rotate-leave-active {
  transition: all 0.4s;
}
.rotate-enter-from,
.rotate-leave-to {
  opacity: 0;
  transform: rotate3d(1, 1, 1, 15deg);
}
</style>
```
::

导航到关于页面时会增加 3D 旋转效果：

<video controls class="rounded" poster="https://res.cloudinary.com/nuxt/video/upload/v1665063233/nuxt3/nuxt3-page-transitions-cutom.jpg">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1665063233/nuxt3/nuxt3-page-transitions-cutom.mp4" type="video/mp4">
</video>

## 布局过渡

你可以启用布局过渡，为所有[布局](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/layouts)应用自动过渡效果。

```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
  app: {
    layoutTransition: { name: 'layout', mode: 'out-in' },
  },
})
```

要开始为页面和布局之间添加过渡，请将以下 CSS 添加到你的 [`app.vue`](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/app) 中：

::code-group
```vue [app.vue]
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

<style>
.layout-enter-active,
.layout-leave-active {
  transition: all 0.4s;
}
.layout-enter-from,
.layout-leave-to {
  filter: grayscale(1);
}
</style>
```

```vue [layouts/default.vue]
<template>
  <div>
    <pre>默认布局</pre>
    <slot />
  </div>
</template>

<style scoped>
div {
  background-color: lightgreen;
}
</style>
```

```vue [layouts/orange.vue]
<template>
  <div>
    <pre>橙色布局</pre>
    <slot />
  </div>
</template>

<style scoped>
div {
  background-color: #eebb90;
  padding: 20px;
  height: 100vh;
}
</style>
```

```vue [pages/index.vue]
<template>
  <div>
    <h1>首页</h1>
    <NuxtLink to="/about">关于页面</NuxtLink>
  </div>
</template>
```

```vue [pages/about.vue]
<script setup lang="ts">
definePageMeta({
  layout: 'orange',
})
</script>

<template>
  <div>
    <h1>关于页面</h1>
    <NuxtLink to="/">首页</NuxtLink>
  </div>
</template>
```
::

下面是在页面导航时产生的效果：

<video controls class="rounded" poster="https://res.cloudinary.com/nuxt/video/upload/v1665065289/nuxt3/nuxt3-layouts-transitions_c9hwlx.jpg">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1665065289/nuxt3/nuxt3-layouts-transitions_c9hwlx.mp4" type="video/mp4">
</video>

类似于 `pageTransition`，你也可以在页面组件中通过 `definePageMeta` 应用自定义的 `layoutTransition`：

```vue twoslash [pages/about.vue]
<script setup lang="ts">
definePageMeta({
  layout: 'orange',
  layoutTransition: {
    name: 'slide-in',
  },
})
</script>
```

## 全局设置

你可以通过 `nuxt.config` 全局自定义这些默认过渡名称。

`pageTransition` 和 `layoutTransition` 两个键都接受 [`TransitionProps`](https://vue.zhcndoc.com/api/built-in-components.html#transition) 作为 JSON 可序列化值，可以传入 `name`，`mode` 以及其他有效的 CSS 过渡属性。

```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
  app: {
    pageTransition: {
      name: 'fade',
      mode: 'out-in' // 默认
    },
    layoutTransition: {
      name: 'slide',
      mode: 'out-in' // 默认
    }
  }
})
```

::warning
如果你更改了 `name` 属性，也需要相应重命名 CSS 类。
::

想要覆盖全局过渡属性，可以使用 `definePageMeta` 在单个 Nuxt 页面中定义页面或布局过渡，并覆盖在 `nuxt.config` 中定义的全局过渡。

```vue twoslash [pages/some-page.vue]
<script setup lang="ts">
definePageMeta({
  pageTransition: {
    name: 'bounce',
    mode: 'out-in' // 默认
  }
})
</script>
```

## 禁用过渡

对于特定路由，可以禁用 `pageTransition` 和 `layoutTransition`：

```vue twoslash [pages/some-page.vue]
<script setup lang="ts">
definePageMeta({
  pageTransition: false,
  layoutTransition: false,
})
</script>
```

或者在 `nuxt.config` 中全局禁用：

```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
  app: {
    pageTransition: false,
    layoutTransition: false,
  },
})
```

## JavaScript 钩子

对于高级用例，你可以使用 JavaScript 钩子为你的 Nuxt 页面创建高度动态和自定义的过渡效果。

这种方式非常适合使用如 [GSAP](https://gsap.com) 这样的 JavaScript 动画库。

```vue twoslash [pages/some-page.vue]
<script setup lang="ts">
definePageMeta({
  pageTransition: {
    name: 'custom-flip',
    mode: 'out-in',
    onBeforeEnter: (el) => {
      console.log('进入前...')
    },
    onEnter: (el, done) => {},
    onAfterEnter: (el) => {},
  },
})
</script>
```

::tip
了解更多关于 `Transition` 组件中可用的附加[JavaScript 钩子](https://vue.zhcndoc.com/guide/built-ins/transition.html#javascript-hooks)。
::

## 动态过渡

要使用条件逻辑应用动态过渡，你可以利用内联[middleware](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/middleware) 为 `to.meta.pageTransition` 指定不同的过渡名称。

::code-group
```vue twoslash [pages/[id\\].vue]
<script setup lang="ts">
definePageMeta({
  pageTransition: {
    name: 'slide-right',
    mode: 'out-in',
  },
  middleware (to, from) {
    if (to.meta.pageTransition && typeof to.meta.pageTransition !== 'boolean') {
      to.meta.pageTransition.name = +to.params.id! > +from.params.id! ? 'slide-left' : 'slide-right'
    }
  },
})
</script>

<template>
  <h1>#{{ $route.params.id }}</h1>
</template>

<style>
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
  transition: all 0.2s;
}
.slide-left-enter-from {
  opacity: 0;
  transform: translate(50px, 0);
}
.slide-left-leave-to {
  opacity: 0;
  transform: translate(-50px, 0);
}
.slide-right-enter-from {
  opacity: 0;
  transform: translate(-50px, 0);
}
.slide-right-leave-to {
  opacity: 0;
  transform: translate(50px, 0);
}
</style>
```

```vue [layouts/default.vue]
<script setup lang="ts">
const route = useRoute()
const id = computed(() => Number(route.params.id || 1))
const prev = computed(() => '/' + (id.value - 1))
const next = computed(() => '/' + (id.value + 1))
</script>

<template>
  <div>
    <slot />
    <div v-if="$route.params.id">
      <NuxtLink :to="prev">⬅️</NuxtLink> |
      <NuxtLink :to="next">➡️</NuxtLink>
    </div>
  </div>
</template>
```
::

页面现在在跳转到下一个 id 时应用 `slide-left` 过渡，跳转到上一个时使用 `slide-right`：

<video controls class="rounded" poster="https://res.cloudinary.com/nuxt/video/upload/v1665069410/nuxt3/nuxt-dynamic-page-transitions.jpg">
<source src="https://res.cloudinary.com/nuxt/video/upload/v1665069410/nuxt3/nuxt-dynamic-page-transitions.mp4" type="video/mp4">
</video>

## 使用 NuxtPage 的过渡

当在 `app.vue` 中使用 `<NuxtPage />` 时，可以通过 `transition` 属性配置过渡效果，从而全局启用过渡。

```vue [app.vue]
<template>
  <div>
    <NuxtLayout>
      <NuxtPage
        :transition="{
          name: 'bounce',
          mode: 'out-in',
        }"
      />
    </NuxtLayout>
  </div>
</template>
```

::note
请记住，这个页面过渡不能通过各个页面的 `definePageMeta` 覆盖。
::

## 视图过渡 API（实验性）

Nuxt 搭载了一个实验性的[**视图过渡 API**](https://developer.chrome.com/docs/web-platform/view-transitions)实现（参考[MDN](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)）。这是一种令人兴奋的新方法，利用原生浏览器过渡（支持在不同页面间过渡不相关的元素等功能）。

你可以访问 <https://nuxt-view-transitions.surge.sh> 查看演示，及查看 [StackBlitz 上的源码](https://stackblitz.com/edit/nuxt-view-transitions)。

Nuxt 集成仍在积极开发中，但可以通过配置文件中的 `experimental.viewTransition` 选项开启：

```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
  experimental: {
    viewTransition: true,
  },
})
```

可选值为：`false`、`true` 或 `'always'`。

若设置为 `true`，Nuxt 将不会在用户的浏览器设置了 `prefers-reduced-motion: reduce` 时应用过渡（推荐）；若设置为 `'always'`，Nuxt 将总是应用过渡，需你自行尊重用户偏好。

默认情况下，视图过渡会为所有[页面](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/pages)启用，但你可以设置不同的全局默认值。

```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
  app: {
    // 全局禁用视图过渡，按页面按需启用
    viewTransition: false,
  },
})
```

你可以在页面的 [`definePageMeta`](https://nuxt.zhcndoc.com/docs/3.x/api/utils/define-page-meta) 中设置 `viewTransition` 键，以覆盖页面的默认 `viewTransition` 值：

```vue twoslash [pages/about.vue]
<script setup lang="ts">
definePageMeta({
  viewTransition: false,
})
</script>
```

::warning
只有在启用了 `experimental.viewTransition` 选项时，才会生效按页面覆写视图过渡。
::

如果你同时使用了 Vue 过渡如 `pageTransition` 和 `layoutTransition`（见上文）实现与视图过渡 API 类似的效果，你可能想在支持新的原生 Web API 的用户浏览器中禁用 Vue 过渡。你可以创建 `~/middleware/disable-vue-transitions.global.ts`，内容如下：

```ts
export default defineNuxtRouteMiddleware((to) => {
  if (import.meta.server || !document.startViewTransition) {
    return
  }

  // 禁用内置的 Vue 过渡
  to.meta.pageTransition = false
  to.meta.layoutTransition = false
})
```

### 已知问题

- 若你在页面 setup 函数中执行数据获取，建议暂时谨慎使用此功能。（设计上，视图过渡在执行时会冻结 DOM 更新。）我们正考虑在 `<Suspense>` 解析完成前的最后时刻限制视图过渡，但在此期间，如果你的情况符合，建议仔细考虑是否采用此功能。


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
