先引入第三方动画库 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
}


加载数据效果 (在客户端请求数据时生效)
)