创建自定义过渡效果

到目前为止,你已经成功地为一个 Nuxt 应用程序创建了多个路由和页面,并添加了一个在页面切换时显示的加载条。这已经让你的应用看起来相当不错了。但这并不是 Nuxt 的全部功能。你可以添加更令人惊叹的效果和页面之间的过渡。这就是页面组件中的 transition 属性(例如,/pages/about.vue)以及 Nuxt 配置文件中的 pageTransitionlayoutTransition 选项发挥作用的地方。

我们可以通过 Nuxt 配置文件全局应用过渡,或者在特定的页面上应用。我们将引导你了解这个主题。然而,要理解 Nuxt 中的过渡是如何工作的,我们首先应该理解它在 Vue 中是如何工作的,然后我们才能学习如何在路由改变时在我们的页面上实现它。让我们开始吧。

理解 Vue 过渡

Vue 依赖于 CSS 过渡,并使用 <transition> Vue 组件包裹 HTML 元素或 Vue 组件来添加 CSS 过渡,如下所示:

<transition>
  <p>hello world</p>
</transition>

你可以看到这有多简单——你可以像切蛋糕一样轻松地用 <transition> 组件包裹任何元素。当这种情况发生时,Vue 将在适当的时机添加和移除以下 CSS 过渡类到该元素上:

  • .v-enter.v-leave 类定义了你的元素在过渡开始之前的外观。

  • .v-enter-to.v-leave-to 类是你的元素的 “完成” 状态。

  • .v-enter-active.v-leave-active 类是元素的活动状态。

这些类是 CSS 过渡发生的地方。例如,你在 HTML 页面中进行的过渡可能如下所示:

.element {
  opacity: 1;
  transition: opacity 300ms;
}
.element:hover {
  opacity: 0;
}

如果我们将上述过渡 “翻译” 到 Vue 上下文中,我们将得到以下内容:

.v-enter,
.v-leave-to {
  opacity: 0;
}
.v-leave,
.v-enter-to {
  opacity: 1;
}
.v-enter-active,
.v-leave-active {
  transition: opacity 300ms;
}

我们可以用下面的图表简单地可视化这些 Vue 过渡类:

image 2025 04 17 21 42 48 333

Vue 默认使用 v- 作为过渡类的前缀,但如果你想更改此前缀,只需在 <transition> 组件上使用 name 属性来指定一个名称——例如,<transition name="fade">;然后,你可以 “重构” 你的 CSS 过渡类,如下所示:

.fade-enter,
.fade-leave-to {
  opacity: 0;
}
.fade-leave,
.fade-enter-to {
  opacity: 1;
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity 300ms;
}

让我们通过以下步骤将上述过渡应用到一个简单的 Vue 应用程序中:

  1. 创建两个简单的路由,并将 <router-view> 组件包裹在一个 <transition> 组件中,如下所示:

    <div id="app">
      <p>
        <router-link to="/about">About</router-link>
        <router-link to="/contact">Contact</router-link>
      </p>
      <transition name="fade" mode="out-in">
        <router-view></router-view>
      </transition>
    </div>
  2. 添加一个包含上述 fade- CSS 过渡类的 <style> 块:

    <style type="text/css">
        .fade-enter,
        //...
    </style>

    当你在浏览器中运行该应用程序时,你可以看到在切换路由时,路由组件需要 300 毫秒的时间来淡入和淡出。

    你可以在我们的 GitHub 仓库中的 /chapter-4/vue/transitions/basic.html 中找到这个示例。

你可以看到,一个过渡需要一些 CSS 类才能工作,但对于一个 Vue 应用程序来说,它们并不难掌握。现在,让我们在下一节中看看如何在 Nuxt 中应用过渡。

使用 pageTransition 属性创建过渡效果

Nuxt 中,不再需要 <transition> 组件。它默认已为您添加,因此您只需在 /assets/ 目录下或任何特定页面的 <style> 块中创建过渡效果即可。pageTransition 属性用于 Nuxt 配置文件中,以设置页面过渡的默认属性。Nuxt 中过渡属性的默认值如下:

{
  name: 'page',
  mode: 'out-in'
}

因此,Nuxt 默认使用 page- 作为过渡类名的前缀,而不是 Vue 使用的 v- 前缀。Nuxt 中默认的过渡模式设置为 out-in。让我们通过创建一个应用于所有页面的全局过渡和一个应用于特定页面的局部过渡来了解如何在 Nuxt 中实现过渡效果,步骤如下:

  1. /assets/ 目录下创建一个 transitions.css 文件,并添加以下过渡效果:

    /* assets/css/transitions.css */
    .page-enter,
    .page-leave-to {
      opacity: 0;
    }
    .page-leave,
    .page-enter-to {
      opacity: 1;
    }
    .page-enter-active,
    .page-leave-active {
      transition: opacity 300ms;
    }
  2. 将上述 CSS 过渡资源的路径添加到 Nuxt 配置文件中:

    // nuxt.config.js
    export default {
      css: [
        'assets/css/transitions.css'
      ]
    }
  3. 请记住,默认前缀是 page-,如果您想使用不同的前缀,可以使用 Nuxt 配置文件中的 pageTransition 属性来更改该前缀:

    // nuxt.config.js
    export default {
      pageTransition: 'fade',
      // 或
      pageTransition: {
        name: 'fade',
        mode: 'out-in'
      }
    }
  4. 然后,将所有默认类名中的前缀更改为 fade,如下所示:

    /* assets/css/transitions.css */
    .fade-enter,
    .fade-leave-to {
      opacity: 0;
    }

    此示例将在路由更改时将过渡效果全局应用于所有页面。

  5. 然而,如果我们想对特定页面应用不同的过渡效果或覆盖页面中的全局过渡,可以在该页面的 transition 属性中进行设置,如下所示:

    // pages/about.vue
    export default {
      transition: {
        name: 'fade-about',
        mode: 'out-in'
      }
    }
  6. 然后,在 transitions.css 中创建 fade-aboutCSS 过渡效果:

    /* assets/css/transitions.css */
    .fade-about-enter,
    .fade-about-leave-to {
      opacity: 0;
    }
    .fade-about-leave,
    .fade-about-enter-to {
      opacity: 1;
    }
    .fade-about-enter-active,
    .fade-about-leave-active {
      transition: opacity 3s;
    }

在此示例中,about 页面淡入和淡出需要 3 秒,而其余页面则需要 300 毫秒。

您可以在我们的 GitHub 仓库中的 /chapter-4/nuxt-universal/transition/page-transitionproperty/ 找到此特定于页面的示例和全局示例。

您可以看到,Nuxt 再次为您简化了一些重复性任务,并增加了为您创建自定义前缀类名过渡效果的灵活性。更重要的是,您甚至可以在布局之间创建过渡效果!让我们在下一节中了解如何实现。

使用 layoutTransition 属性创建过渡效果

CSS 过渡不仅适用于页面组件,也适用于布局。layoutTransition 属性的默认值为:

{
  name: 'layout',
  mode: 'out-in'
}

因此,布局过渡类名的默认前缀是 layout-,默认的过渡模式是 out-in。让我们通过以下步骤创建一个应用于所有布局的全局过渡,来看看如何实现过渡:

  1. /layouts/ 目录下创建 about.vueuser.vue 布局文件,内容如下:

    // layouts/about.vue
    <template>
      <div>
        <p>About layout</p>
        //...
        <nuxt />
      </div>
    </template>
  2. 将上述布局应用于 /pages/ 目录下的 about.vueusers.vue 页面,内容如下:

    // pages/about.vue
    export default {
      layout: 'about'
    }
  3. /assets/ 目录下创建一个 transitions.css 文件,并添加以下过渡效果:

    // assets/css/transitions.css
    .layout-enter,
    .layout-leave-to {
      opacity: 0;
    }
    .layout-leave,
    .layout-enter-to {
      opacity: 1;
    }
    .layout-enter-active,
    .layout-leave-active {
      transition: opacity .5s;
    }
  4. 将上述 CSS 过渡资源文件的路径添加到 Nuxt 配置文件中:

    // nuxt.config.js
    export default {
      css: [
        'assets/css/transitions.css'
      ]
    }
  5. 默认的前缀是 layout-,但如果你想使用不同的前缀,可以使用 Nuxt 配置文件的 layoutTransition 属性来更改它:

    // nuxt.config.js
    export default {
      layoutTransition: 'fade-layout',
      // 或
      layoutTransition: {
        name: 'fade-layout',
        mode: 'out-in'
      }
    }
  6. transitions.css 中所有默认的类名前缀更改为 fade-layout,如下所示:

    // assets/css/transitions.css
    .fade-layout-enter,
    .fade-layout-leave-to {
      opacity: 0;
    }

    在这个例子中,整个布局(包括导航)的淡入和淡出需要 0.5 秒。当你导航到使用不同布局的页面之间时,你会看到这个过渡效果;但是,如果你在两个使用相同布局的页面之间导航,例如从 //contact,你将不会看到上述布局过渡,因为它们都使用相同的布局,即 /layouts/default.vue

    你可以在我们的 GitHub 仓库的 /chapter-4/nuxtuniversal/transition/layout-transition-property/ 目录下找到这个例子。

再一次,你可以看到创建布局过渡非常容易,并且你可以像页面过渡一样自定义它们的前缀类名。除了使用 CSS 过渡来过渡页面和布局之外,我们还可以使用 CSS 动画。让我们在下一节中了解如何使用 CSS 动画。

使用 CSS 动画创建过渡效果

CSS 过渡是一种仅在两个状态(开始结束)之间执行的动画。但是,当您需要更多中间状态时,您应该使用 CSS 动画,这样您可以通过在开始和结束状态之间添加具有不同百分比的多个关键帧来获得更多控制。看下面的例子:

@keyframes example {
  0% { /* 第一个关键帧或开始状态 */
    background-color: red;
  }
  25% { /* 第二个关键帧 */
    background-color: yellow;
  }
  50% { /* 第三个关键帧 */
    background-color: blue;
  }
  100% { /* 第四个关键帧,结束状态 */
    background-color: green;
  }
}

0% 是动画的开始状态,而 100% 是动画的结束状态。您可以通过添加递增的百分比(例如,10%20%30% 等)在两个状态之间添加更多中间状态。然而,CSS 过渡不具备添加这些关键帧的能力。因此,我们可以说 CSS 过渡是 CSS 动画的一种简单形式。

由于 CSS 过渡 “实际上” 是 CSS 动画,我们可以像在 Vue/Nuxt 应用程序中应用 CSS 过渡一样应用 CSS 动画。让我们通过以下步骤来了解如何操作:

  1. 将以下 CSS 动画代码添加到 transitions.css 文件中,就像您在前一节中所做的那样:

    /* assets/css/transitions.css */
    .bounce-enter-active {
      animation: bounce-in .5s;
    }
    .bounce-leave-active {
      animation: bounce-in .5s reverse;
    }
    @keyframes bounce-in {
      0% {
        transform: scale(0);
      }
      50% {
        transform: scale(1.5);
      }
      100% {
        transform: scale(1);
      }
    }
  2. Nuxt 配置文件中,将全局默认的 page- 前缀更改为 bounce-

    // nuxt.config.js
    export default {
      pageTransition: 'bounce'
    }

    添加完上述代码后,刷新您的浏览器,您将看到在页面切换时页面会弹跳进入和退出。

    您可以在我们的 GitHub 仓库的 /chapter-4/nuxt-universal/transition/css-animations/ 中找到此示例。

根据您想要动画的复杂性和详细程度,以及您的 CSS 动画技能水平,您可以为您的页面和布局创建非常惊艳的过渡效果。您只需要专注于编写代码并通过 Nuxt 配置文件注册它,然后 Nuxt 将负责在适当的时间添加和删除 CSS 动画类的其余工作。但是 JavaScript 呢?我们可以使用 jQuery 或任何其他 JavaScript 动画库来为我们的页面和布局创建过渡动画吗?答案是肯定的。让我们在下一节中了解如何操作。

使用 JavaScript 钩子创建过渡效果

除了使用 CSS 进行过渡之外,你还可以通过在 Vue 应用的 <transition> 组件中添加以下钩子来使用 JavaScript 进行过渡:

<transition
  v-on:before-enter="beforeEnter"
  v-on:enter="enter"
  v-on:after-enter="afterEnter"
  v-on:enter-cancelled="enterCancelled"
  v-on:before-leave="beforeLeave"
  v-on:leave="leave"
  v-on:after-leave="afterLeave"
  v-on:leave-cancelled="leaveCancelled"
>
  //..
</transition>

请注意,你也可以声明钩子而不添加开头的 v-on。因此,将钩子写成 :before-enter 与写成 v-on:before-enter 是相同的。

然后,在 JavaScript 方面,你应该在 methods 属性中拥有以下默认方法来与前面的钩子对应:

methods: {
  beforeEnter (el) { ... },
  enter (el, done) {
    // ...
    done()
  },
  afterEnter (el) { ... },
  enterCancelled (el) { ... },
  beforeLeave (el) { ... },
  leave (el, done) {
    // ...
    done()
  },
  afterLeave (el) { ... },
  leaveCancelled (el) { ... }
}

你可以单独使用这些 JavaScript 钩子,也可以将它们与 CSS 过渡一起使用。如果单独使用它们,则必须在 enterleave 钩子(methods)中使用 done 回调,否则这两个方法将同步运行,并且你尝试应用于此的动画或过渡将立即结束。此外,如果单独使用它们,你还应该在 <transition> 包裹器上使用 v-bind:css="false",这样 Vue 就会安全地忽略你的元素,以防你的应用中也存在 CSS 过渡,但它被用于其他元素。让我们通过以下步骤创建一个使用这些 JavaScript 钩子的简单 Vue 应用:

  1. 将以下 CDN 链接添加到 HTML 的 <head> 块中:

    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  2. 将应用标记和带有钩子的 <transition> 组件添加到 <body> 块中:

    <div id="app">
      <p>
        <router-link to="/about">About</router-link>
        <router-link to="/contact">Contact</router-link>
      </p>
      <transition
        appear
        v-bind:css="false"
        v-on:before-enter="beforeEnter"
        v-on:enter="enter"
        v-on:leave="leave"
        v-on:after-leave="afterLeave"
      >
        <router-view></router-view>
      </transition>
    </div>
  3. <script> 块中添加以下方法,以与前面的钩子协调:

    const app = new Vue({
      name: 'App',
      methods: {
        beforeEnter (el) { $(el).hide() },
        enter (el, done) {
          $(el).fadeTo('slow', 1)
          done()
        },
        leave (el, done) {
          $(el).fadeTo('slow', 0, function () {
            $(el).hide()
          })
          done()
        },
        afterLeave (el) { $(el).hide() }
      }
    }).$mount('#app')

    在这个例子中,我们使用 jQueryfadeTo 方法来控制过渡,而不是使用纯 CSS。你应该看到路由组件在切换时像 .v-enter.v-leave CSS 过渡一样淡入和淡出。

    你可以在我们的 GitHub 仓库的 /chapter-4/vue/transition/jshooks.html 中找到这个例子。

Nuxt 中,我们不需要在 <transition> 组件中定义 JavaScript 钩子,只需要在 Nuxt 配置文件中的 pageTransition/pages/ 目录下任何 .vue 文件中的 transition 中定义 JavaScript 方法。让我们通过以下步骤在 Nuxt 应用中创建一个快速示例:

  1. 在你的终端通过 npm 安装 jQuery

    $ npm i jquery
  2. 由于我们将在 Nuxt 配置文件和其他页面中使用 jQuery,我们可以通过 Nuxt 配置文件中的 webpack 全局加载 jQuery

    // nuxt.config.js
    import webpack from 'webpack'
    export default {
      build: {
        plugins: [
          new webpack.ProvidePlugin({
            $: "jquery"
          })
        ]
      }
    }
  3. Nuxt 配置文件的 pageTransition 选项中创建一个使用 jQuery 的全局过渡:

    // nuxt.config.js
    export default {
      pageTransition: {
        mode: 'out-in',
        css: false,
        beforeEnter: el => { $(el).hide() },
        enter: (el, done) => {
          $(el).fadeTo(1000, 1)
          done()
        },
        //...
      }
    }

    这个例子将在路由更改时将过渡全局应用于所有页面。此外,我们通过将 css 选项设置为 false 来关闭 CSS 过渡。

    请注意,我们使用对象键来编写 JavaScript 函数,作为与 transition 组件中的属性钩子关联的替代方法。

  4. /pages/ 目录下创建一个 about.vue 页面,并通过 about.vue 页面上的 transition 属性应用不同的过渡来覆盖前面的全局过渡:

    // pages/about.vue
    export default {
      transition: {
        mode: 'out-in',
        css: false,
        beforeEnter: el => { $(el).hide() },
        enter: (el, done) => {
          $(el).fadeTo(3000, 1)
          done()
        },
        //...
      }
    }

    因此,在这个例子中,这个特定页面的过渡将花费 3 秒,而其他页面将花费 1 秒。

    请注意,如果 jQuery 没有在 Nuxt 配置文件中加载,你必须将其导入到 .vue 页面;例如,假设你只想在这个特定页面上设置过渡:

    // pages/about.vue
    import $ from 'jquery'
    export default {
      transition: {
        beforeEnter (el) { $(el).hide() },
        //...
      }
    }

    一旦代码就位,刷新你的浏览器,你应该会看到页面在页面之间切换时像 Vue 应用一样淡入和淡出。

    你可以在我们的 GitHub 仓库的 /chapter-4/nuxt-universal/transition/js-hooks/ 中找到这个例子。

做得好;你已经完成了关于在 Nuxt 中创建过渡的部分!你可以看到,JavaScript 是在你的 Nuxt 应用中编写过渡和动画的另一种好方法。但在结束本章之前,让我们看看我们在这些关于过渡的部分中一直看到的过渡模式。那么,让我们来看看它们是用来做什么的。

请注意,尽管现在不鼓励使用 jQuery,但本书偶尔会使用它,因为它是在上一章中你了解到的 Foundation 的依赖项。因此,我们有时会重复使用它。或者,你可以使用 Anime.js 来制作 JavaScript 动画。有关此库的更多信息,请访问 https://animejs.com/

理解过渡模式

你可能已经好奇 mode="out-in" (在 Vue 中) 或 mode: 'out-in' (在 Nuxt 中) 是什么 – 例如,在我们之前的 Vue 应用中,它们出现在 <div>about</div><div>contact</div> 组件中。它们之所以在那里,是因为 <div>about</div><div>contact</div> 之间的过渡是同时渲染的。这是 <transition> 的默认行为:进入和离开同时进行。但有时,你可能不希望这种同步过渡,因此 Vue 提供了以下过渡模式来解决这个问题:

  • in-out 模式

    此模式用于让新元素先过渡进入,直到其过渡完成,然后当前元素才会过渡离开。

  • out-in 模式

    此模式用于让当前元素先过渡离开,直到其过渡完成,然后新元素才会过渡进入。

因此,你可以通过以下方式使用这些模式:

  • Vue.js 应用中,如下使用:

    <transition name="fade" mode="out-in">
      //...
    </transition>
  • Nuxt 应用中,如下使用:

    export default {
      transition: {
        name: 'fade',
        mode: 'out-in'
      }
    }
  • JavaScript 钩子中,如下使用:

    export default {
      methods: {
        enter (el, done) {
          done() // 相当于 mode="out-in"
        },
        leave (el, done) {
          done() // 相当于 mode="out-in"
        }
      }
    }

关于为我们的 Nuxt/Vue 应用创建自定义过渡,我们已经走了很长一段路。从本章所学的知识来看,你现在可以制作一些不错的过渡和动画了。因此,由于本书篇幅有限,我们不会再过多地介绍这个主题,但有关 Vue 过渡和动画的更多信息和进一步阅读,请访问 https://vuejs.org/v2/guide/transitions.html

现在让我们总结一下你在本章中学到的内容!