本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
nuxt 框架与传统的 Vue SPA 相比,使用 SSR 将带来巨大的 SEO 提升,但基本框架依然是Vue。但是因为要做SEO,所以基本是基于服务端渲染的,有时路由跳转会等待一段时间,客户端最好进行提示或者展示过场动画,提示用户正在跳转。
本文记叙个人在nuxt中,等待路由跳转,使用过场动画的思路。
基本思路
思路主要是下面两个方面:
动画时间:既然是在路由跳转时启用的,那么自然是在全局路由守卫中对过场动画进行添加删除。
过场动画:因为是在全局路由守卫中使用,所以使用JS将过程动画封装成函数,进行调用,可以借鉴自定义vue指令的思路。
代码
- 首先,创建过场动画的
vue组件,这个因人而异,不再赘述。 - 创建一个
.js文件,目的是将上面过场动画的Vue组件封装成函数调用的形式,然后直接在路由守卫中调用函数即可。
- 一开始,先导入
Vue和过场动画组件,然后创建过场动画的组件构造器:
import Vue from 'vue'
import RouterAnimate from './routerAnimate.vue'
// 创建组件构造器
const constructor = Vue.extend(RouterAnimate)
- 创建展示过场动画的函数,主要是以下步骤:
- 判断环境,如果是在服务端,不执行任何操作
- 确定过场动画的父级
DOM元素,本文是document.body- 根据上面的构造器创建过场动画的实例
- 在实例上添加关闭函数,这个因人而异,但既然时候用函数添加的过场动画,自然用函数关闭也是比较合理
- 最后就是将实例中
$el放到父级DOM元素里面,返回实例用于后续的关闭 整体代码:
const showRouterAnimate = () => {
// 判断环境是否在服务端,如果是,不执行任何操作
if (Vue.prototype.$isServer) {
return false
}
// 确定过场动画的父级`DOM`元素
const parent = document.body
// 生成过场动画的实例
const instance = new constructor({ el: document.createElement('div') });
// 挂载关闭事件
instance.close = close(parent)
// 挂载组件元素
parent.appendChild(instance.$el)
// 添加类名,用于让过程动画逐渐出现,因人而异
instance.$el.className += ' opacity-1'
// 返回实例
return instance
}
- 接下来就是关闭函数,在上面的代码中可以看到,因为在实例上挂载了关闭函数,所以在路由守卫中只要得到过场动画的实例,就可以执行关闭。本文为了让过场动画有一个逐渐消失的过程,所以不会立即从
HTML中删除过场动画,而是先加上消失的class,使用定时器延时删除:
function close (parent) {
// 返回实例对应的关闭函数
return () => {
// 间隔时间
const time = 1500
// 判断父级元素和实例是否存在
if (parent && this.$el) {
// 添加消失的类名
this.$el.className = 'opacity-0'
// 创建定时器,等过场动画消失后,从html删除过场动画的元素
const timerEndHidden = setTimeout(() => {
clearTimeout(timerEndHidden)
// 从html删除元素
parent.removeChild(this.$el)
}, time)
}
}
}
- 然后就是使用,因为不能允许有多个过场动画的存在,所以使用前必须判断,因为可以进一步封装成
JSON或者Class来使用:
// 路由跳转过场动画
const routerAnimateInstance = {
// 过场动画实例
instance: null,
// 开启函数
open () {
// 如果已经存在过场动画,则关闭
this.instance && this.close()
// 开启新的过场动画
this.instance = showRouterAnimate()
},
// 关闭
close () {
// 判断实例是否存在
if (this.instance) {
// 关闭实例
this.instance.close()
// 实例置为null
this.instance = null
}
}
}
这样,直接调用JSON中函数,可以保证过场动画不会出现多个。
- 最后就是在全局路由守卫中导入使用:
export default ({ app, store, redirect, req, route }) => {
app.router.beforeEach((to, from, next) => {
// 路由跳转前 开启动画
routerAnimateInstance.open()
next()
})
app.router.afterEach((to, from) => {
// 路由条状完成 关闭动画
routerAnimateInstance.close()
})
}