在 Vue Router 中,你可以通過過渡動效(Transition Effects)為路由切換添加平滑的過渡效果,從而提升用戶體驗。過渡動效可以使用 Vue 的 <transition>
組件和 CSS 過渡來實現。
基本使用:
- 對導航使用動畫,需要
v-slot
API:
<router-view #default="{route,Component}"><transition :enter-active-class="`animate__animated ${route.meta.transition}`"><component :is="Component"></component></transition></router-view>
- 以上會對所有的路由使用相同的過渡。如果你想讓每個路由的組件有不同的過渡,可以將元信息和動態的
name
結合在一起,放在<transition>
上:
declare module 'vue-router'{interface RouteMeta {title:string,transition:string,}
}const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',component: () => import('@/views/Login.vue'),meta:{title:"登錄頁面",transition:"animate__fadeInUp",}},{path: '/index',component: () => import('@/views/Index.vue'),meta:{title:"首頁!!!",transition:"animate__bounceIn",}}]
})
用法總結,在路由切換時添加過渡動效:
-
首先,你需要在你的 Vue 項目中導入
<transition>
組件。 -
在需要添加過渡動效的地方(通常是路由的
<router-view>
),使用<transition>
包裹你的內容。 -
為
<transition>
組件添加一個 CSS 類,以便在路由切換時觸發過渡效果。
實例演示(以下實例會用到 Animation.css
庫):
index.ts
import { createRouter, createWebHistory } from 'vue-router'declare module 'vue-router' {interface RouteMeta {title: string,transition: string}
}export const router = createRouter({// import.meta.env.BASE_URL 應用的基本 URL。基本 URL 是指在你的應用部署到某個域名或子路徑時,URL 的起始部分。例如,如果你的應用部署在 https://example.com/myapp/ 這個路徑下,那么 import.meta.env.BASE_URL 就會是 /myapp/。history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',component: () => import('@/views/Login.vue'),meta: {title: "登錄頁",transition: "animate__fadeIn"}},{path: '/index',component: () => import('@/views/Index.vue'),meta: {title: "首頁",transition: "animate__bounceOut"}},],
})
loadingBar.vue
<template><div class="wraps"><div ref="bar" class="bar"></div></div>
</template><script setup lang="ts">
import { ref, onMounted } from 'vue'
let speed = ref<number>(1)
let bar = ref<HTMLElement>()
let timer = ref<number>(0)
const startLoading = () => {speed.value = 1let dom = bar.value as HTMLElementtimer.value = window.requestAnimationFrame(function fn() {if (speed.value < 90) {speed.value += 1;dom.style.width = speed.value + '%'timer.value = window.requestAnimationFrame(fn)} else {speed.value = 1window.cancelAnimationFrame(timer.value)}})
}
const endLoading = () => {let dom = bar.value as HTMLElementsetTimeout(() => {window.requestAnimationFrame(() => {speed.value = 100dom.style.width = speed.value + '%'})}, 500)}defineExpose({ startLoading, endLoading })
</script><style scoped lang="less">
.wraps {width: 100%;position: fixed;height: 10px;top: 0;.bar {height: inherit;width: 0;background-color: #409eff;}
}
</style>
Index.vue
<template><h1>我進來啦</h1>
</template><script setup lang="ts"></script><style scoped></style>
Login.vue
<template><div class="login"><el-card class="box-card"><el-form ref="form" :rules="rules" :model="formInline" class="demo-form-inline"><el-form-item prop="user" label="賬號:"><el-input v-model="formInline.user" placeholder="請輸入賬號" /></el-form-item><el-form-item prop="password" label="密碼:"><el-input v-model="formInline.password" placeholder="請輸入密碼" type="password"></el-input></el-form-item><el-form-item><el-button type="primary" @click="onSubmit">登錄</el-button></el-form-item></el-form></el-card></div>
</template><script setup lang="ts">
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import type { FormItemRule, FormInstance } from 'element-plus';
import { ElMessage } from 'element-plus'const router = useRouter()
type Form = {user: string,password: string
}
type Rules = {[k in keyof Form]?: Array<FormItemRule>
}
const formInline = reactive<Form>({user: '',password: '',
})
const form = ref<FormInstance>()
const rules = reactive({user: [{required: true,message: '請輸入賬號',type: 'string',}],password: [{required: true,message: '請輸入密碼',type: 'string',}]
})const onSubmit = () => {console.log('submit!', form.value)form.value?.validate((validate)=>{if (validate) {router.push('/index')localStorage.setItem('token', '1')} else {ElMessage.error('賬號或密碼錯誤')}})}
</script><style scoped lang="less">
.login {height: 100%;display: flex;justify-content: center;align-items: center;
}
</style>
App.vue
<template><router-view #default="{ Component, route }"><transition :enter-active-class="`animate__animated ${route.meta.transition}`"><component :is="Component"></component></transition></router-view>
</template><script setup lang="ts">
import 'animate.css'</script><style>
/* 注意 style 標簽 別加 scoped 不然設置寬高不生效 */
* {margin: 0;padding: 0;
}
html, body, #app {height: 100%;overflow: hidden;
}
</style>
main.ts
import { createApp,createVNode,render } from 'vue'
import App from './App.vue'
import {router} from './router'
// import 引入
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import loadingBar from './components/loadingBar.vue'const Vnode = createVNode(loadingBar)
render(Vnode,document.body)
const app = createApp(App)
app.use(router)
// use 注入 ElementPlus 插件
app.use(ElementPlus)const whiteList = ['/']// beforeEach 可以定義不止一個,vue會收集所有定義的路由鉤子,所以next的作用不應該是跳轉,而是使步驟進行到下一個你定義的鉤子
router.beforeEach((to, from, next) => {document.title = to.meta.titleVnode.component?.exposed?.startLoading()// token每次都要跟后端校驗一下是否過期if(whiteList.includes(to.path) || localStorage.getItem('token')){next()}else{next('/')}
})router.afterEach((to, from) => {Vnode.component?.exposed?.endLoading()
})
app.mount('#app')
可以發現在切換到首頁或者登錄頁都有過渡動效。