在 Nuxt 中自定义 loading 加载效果,搭配 nuxt/axios 拦截器
先引入第三方动画库 lottiefiles
-
我的版本是 ^1.5.6
npm install @lottiefiles/lottie-player
-
在 plugins 下创建一个 Loading 文件夹,Loading 下创建一个 lottiePlayer.js 全局引入该模块
// 引入lottiefiles
import '@lottiefiles/lottie-player'
- 在 nuxt.config.js 中的 plugins 模块中引入
module.exports = {
plugins: [
// 自定义loading
{ src: '~/plugins/Loading/lottiePlayer.js', ssr: false }, // 使用了window对象,所以关闭服务端渲染
],
}
在 plugins/Loading 下创建一个 loading.vue,并挂载到 Vue 实例原型上
- Loading/loading.vue
<template>
<div v-show="loading" class="loading-page">
<lottie-player
v-show="isShow"
class="lottie"
src="https://assets8.lottiefiles.com/packages/lf20_nm1huacl.json"
background="transparent"
speed="1.5"
style="width: 300px; height: 300px"
loop
autoplay
/>
<lottie-player
v-show="!isShow"
class="lottie"
src="https://assets4.lottiefiles.com/private_files/lf30_weidduaf.json"
background="transparent"
speed="1"
style="width: 300px; height: 300px"
loop
autoplay
/>
</div>
</template>
<script>
export default {
data: () => ({
loading: false,
// 随机展示两者中的一个
isShow: Math.floor(Math.random() * 2)
})
}
</script>
<style lang="less" scoped>
.loading-page {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
overflow: hidden;
z-index: 99999;
.lottie {
position: absolute;
top: 47%;
left: 51%;
transform: translate(-51%, -47%);
}
&::before {
position: absolute;
content: "";
width: 100%;
height: 100%;
// background-color: #bebebe;
background: rgba(235, 235, 235, 0.8);
backdrop-filter: blur(5px);
filter: Alpha(Opacity=90);
opacity: 0.9;
}
}
</style>
- Loading/index.js 挂载
import Vue from 'vue'
// 自定义loading
import Loading from './loading.vue'
// 空实例
let instance = null
const loading = {
install(Vue) {
if (!instance) {
// 构造器
const MyLoading = Vue.extend(Loading)
instance = new MyLoading({
// 创建一个div,挂载上去
el: document.createElement('div')
})
document.body.appendChild(instance.$el)
}
instance.loading = false // 默认为false
// 自定义方法
const customMethods = {
start() {
instance.loading = true
},
finish() {
instance.loading = false
}
}
// 挂载到自定义方法vue上
if (!Vue.$loading) {
Vue.$loading = customMethods
// 挂载到原型上
Vue.prototype.$loading = Vue.$loading
} else {
console.log('$loading方法被占用')
}
}
}
Vue.use(loading)
- 然后在 nuxt.config.js 中的 plugins 注册一下,注意:要取消 nuxt 原来的 loading 效果,因为它原来在 n u x t 挂 载 的 也 是 nuxt 挂载的也是 nuxt 挂载的也是 loading
//nuxt.config.js
export default {
// 使用自定义loading,正好nuxt不用在this实例上添加$loading了
loading: false,
plugins: [
// 自定义loading
{ src: '~/plugins/Loading/lottiePlayer.js', ssr: false }, // 使用了window对象
{ src: '~/plugins/Loading/index.js', ssr: false }, // 使用了document对象
],
}
在 axios 拦截器中添加 loading
//api/interceptor.js
import Vue from 'vue'
import {
message
} from 'ant-design-vue'
export default ({ store, route, redirect, $axios, req }) => {
$axios.onRequest(config => {
// 开启加载, 只有客户端才有window document 对象。配合plugins只在客户端中开启
if (process.client) {
Vue.prototype.$loading.start()
}
const accessToken = store.state.accessToken
// 请求头添加token
if (accessToken) {
// Authorization: Bearer token
config.headers.Authorization = `Bearer ${accessToken.replace(/"/g, '')}`
}
return config
})
$axios.onResponse(res => {
// 客户端拦截打印 结束加载
if (process.client) {
console.log('客户端拦截数据:', res.data)
setTimeout(() => {
Vue.prototype.$loading.finish()
}, 300)
}
if ((res.status !== 200 || res.data.code !== 20000) && process.client) {
// !!! 服务端错误提示不生效,因为服务端没有document对象,所以在asyncData中请求的数据要注意!!!
message.error(res.message || res.data.message || '数据响应异常', 1)
}
// 服务端不能执行message
if (res?.data.code !== 20000 && process.server) {
console.error('服务端:', res?.data.message || '数据响应异常')
console.error('服务端错误数据:', res.data)
}
return res.data
})
$axios.onError(error => {
// 结束加载, 只有客户端才有window document 对象。配合plugins只在客户端中开启
if (process.client) {
Vue.prototype.$loading.finish()
}
const { response: res } = error
// 权限过期 -> 重定向
if (res.status === 401 && res.data.code === 1401) {
let isLock = true
if (isLock && store.state.refreshToken) {
isLock = false
// 发送请求到认证客户端,通过刷新令牌获取新令牌
redirect(`${process.env.authURL}/refresh?redirectURL=${redirectURL(route, req)}`)
} else {
isLock = true
// 没有刷新令牌,跳转到登录页
// 重置用户状态
// console.log('跳转到登录页')
store.commit('RESET_USER_STATE')
// 跳转到登录页
redirect(`${process.env.authURL}?redirectURL=${redirectURL(route, req)}`)
}
message.error(res.data.message, 1)
if (process.client) {
Vue.prototype.$loading.start()
}
return Promise.reject(res.data.message || 'Error')
}
message.error(res.data.message, 1)
return Promise.reject('令牌过期,重新登录')
})
}
// 获取重定向地址
const redirectURL = (route, req) => {
// 客户端
if (process.client) {
return window.location.href
}
// 服务端
return `${process.env.NODE_ENV === 'production' ? 'https://' : 'http://'}` + req.headers.host + route.fullPath
}
加载数据效果 (在客户端请求数据时生效)
)
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 奇怪的阿峰
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果