跳至主要內容

Loading

chanchaw大约 5 分钟react

概述

发起一个异步请求就显示本自定义组件(Loading组件),得到响应后隐藏该组件。支持同时发起多个异步请求,只会显示一个 Loading 组件,并且在最后一个请求结束后隐藏组件

源码

原本想封装一个单独的 Loading 组件,在其中使用 svg 时有个无法解决的报错,最后的实现是将 svg 放在了 index.html 中,通过 display 控制是否显示,其中 index.html 的完整代码如下,将一个 svg 包裹在 div 中进行控制,要改变 svg 的大小就修改 svg 的属性 widthheight,注意同时通过 css 控制了图标的显示效果

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
    <style>
      .loading {
        animation: rotate linear 2s infinite;
      }
      @keyframes rotate {
        from {
          transform: rotate(0deg);
        }
        to {
          transform: rotate(360deg);
        }
      }

      #loading {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 20px;
        flex-direction: column;
      }
    </style>
  </head>
  <body>
    <div id="root"></div>

    <div id="loading" style="display: none">
      <svg
        t="1723462243452"
        class="icon loading"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="4415"
        id="mx_n_1723462243453"
        width="64"
        height="64"
      >
        <path
          d="M876.864 782.592c3.264 0 6.272-3.2 6.272-6.656 0-3.456-3.008-6.592-6.272-6.592-3.264 0-6.272 3.2-6.272 6.592 0 3.456 3.008 6.656 6.272 6.656z m-140.544 153.344c2.304 2.432 5.568 3.84 8.768 3.84a12.16 12.16 0 0 0 8.832-3.84 13.76 13.76 0 0 0 0-18.56 12.224 12.224 0 0 0-8.832-3.84 12.16 12.16 0 0 0-8.768 3.84 13.696 13.696 0 0 0 0 18.56zM552.32 1018.24c3.456 3.648 8.32 5.76 13.184 5.76a18.368 18.368 0 0 0 13.184-5.76 20.608 20.608 0 0 0 0-27.968 18.368 18.368 0 0 0-13.184-5.824 18.368 18.368 0 0 0-13.184 5.76 20.608 20.608 0 0 0 0 28.032z m-198.336-5.76c4.608 4.8 11.072 7.68 17.6 7.68a24.448 24.448 0 0 0 17.536-7.68 27.456 27.456 0 0 0 0-37.248 24.448 24.448 0 0 0-17.536-7.68 24.448 24.448 0 0 0-17.6 7.68 27.52 27.52 0 0 0 0 37.184z m-175.68-91.84c5.76 6.08 13.824 9.6 21.952 9.6a30.592 30.592 0 0 0 22.016-9.6 34.368 34.368 0 0 0 0-46.592 30.592 30.592 0 0 0-22.016-9.6 30.592 30.592 0 0 0-21.952 9.6 34.368 34.368 0 0 0 0 46.592z m-121.152-159.36c6.912 7.36 16.64 11.648 26.368 11.648a36.736 36.736 0 0 0 26.432-11.584 41.28 41.28 0 0 0 0-55.936 36.736 36.736 0 0 0-26.432-11.584 36.8 36.8 0 0 0-26.368 11.52 41.28 41.28 0 0 0 0 56zM12.736 564.672a42.88 42.88 0 0 0 30.784 13.44 42.88 42.88 0 0 0 30.784-13.44 48.128 48.128 0 0 0 0-65.216 42.88 42.88 0 0 0-30.72-13.44 42.88 42.88 0 0 0-30.848 13.44 48.128 48.128 0 0 0 0 65.216z m39.808-195.392a48.96 48.96 0 0 0 35.2 15.36 48.96 48.96 0 0 0 35.2-15.36 54.976 54.976 0 0 0 0-74.56 48.96 48.96 0 0 0-35.2-15.424 48.96 48.96 0 0 0-35.2 15.424 54.976 54.976 0 0 0 0 74.56zM168.32 212.48c10.368 11.008 24.96 17.408 39.68 17.408 14.592 0 29.184-6.4 39.552-17.408a61.888 61.888 0 0 0 0-83.84 55.104 55.104 0 0 0-39.616-17.408c-14.656 0-29.248 6.4-39.616 17.408a61.888 61.888 0 0 0 0 83.84zM337.344 124.8c11.52 12.16 27.712 19.264 43.968 19.264 16.256 0 32.448-7.04 43.968-19.264a68.672 68.672 0 0 0 0-93.184 61.248 61.248 0 0 0-43.968-19.264 61.248 61.248 0 0 0-43.968 19.264 68.736 68.736 0 0 0 0 93.184z m189.632-1.088c12.672 13.44 30.528 21.248 48.448 21.248s35.712-7.808 48.384-21.248a75.584 75.584 0 0 0 0-102.464A67.392 67.392 0 0 0 575.36 0c-17.92 0-35.776 7.808-48.448 21.248a75.584 75.584 0 0 0 0 102.464z m173.824 86.592c13.824 14.592 33.28 23.104 52.736 23.104 19.584 0 39.04-8.512 52.8-23.104a82.432 82.432 0 0 0 0-111.744 73.472 73.472 0 0 0-52.8-23.168c-19.52 0-38.912 8.512-52.736 23.168a82.432 82.432 0 0 0 0 111.744z m124.032 158.528c14.976 15.872 36.032 25.088 57.216 25.088 21.12 0 42.24-9.216 57.152-25.088a89.344 89.344 0 0 0 0-121.088 79.616 79.616 0 0 0-57.152-25.088c-21.184 0-42.24 9.216-57.216 25.088a89.344 89.344 0 0 0 0 121.088z m50.432 204.032c16.128 17.088 38.784 27.008 61.632 27.008 22.784 0 45.44-9.92 61.568-27.008a96.256 96.256 0 0 0 0-130.432 85.76 85.76 0 0 0-61.568-27.072c-22.848 0-45.44 9.984-61.632 27.072a96.192 96.192 0 0 0 0 130.432z"
          fill="#1677ff"
          p-id="4416"
        ></path>
      </svg>
      <p>加载中...</p>
    </div>

    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

制作组件 src/utils/loadingSVG/index.tsx,代码如下:(目录 loadingSVG 是组件目录)

let count = 0
const showLoading = () => {
  if (count === 0) {
    const loadingDiv = document.getElementById('loading')
    loadingDiv?.style.setProperty('display', 'flex')
  }
  count++
}

const hideLoading = () => {
  count--
  if (count > 0) return
  const loadingDiv = document.getElementById('loading')
  loadingDiv?.style.setProperty('display', 'none')
}

export { showLoading, hideLoading }

在封装的 src/utils/request.ts 中的请求拦截器中调用 showLoading() 显示加载图标,在响应拦截器中通过 hideLoading() 隐藏图标,注意响应拦截器的 error 分支中也要隐藏图标

局部loading

制作.d.ts文件1/4

制作 typings.d.ts 文件,为 axios 扩展自定的属性,全部代码如下。注意由于这里没有使用到导入的 AxiosRequestConfig 要关闭 eslint 的配置项 @typescript-eslint/no-unused-vars 或者修改为 warn

import { AxiosRequestConfig } from 'axios'
declare module 'axios' {
  interface AxiosRequestConfig {
    showLoading?: boolean
    showError?: boolean
  }
}
再加工axios封装工具2/4

之前使用 axios 封装的 src/utils/request.ts 中要添加下面接口

interface IConfig {
  showLoading?: boolean
  showError?: boolean
}

并在本文件中之前已经封装的 getpost 中应用该类型(本封装的工具在 https://gitee.com/chanchaw/reat-manager 中)

export default {
  get<T>(url: string, params?: unknown, options: IConfig = { showLoading: true, showError: true }): Promise<T> {
    return instance.get(url, { params, ...options })
  },
  post<T>(url: string, params?: unknown, options: IConfig = { showLoading: true, showError: true }): Promise<T> {
    return instance.post(url, params, options)
  }
}
封装API中关闭Loading3/4

项目封装的 src/api/index.ts 中可自定义关闭使用全局 loading 功能

import request from '@/utils/request'
import { Login } from '@/types/api'

export default {
  login(params: Login.email) {
    return request.post('/user/login/email/password', params, { showLoading: false })
  }
}
局部使用loading4/4

在具体的业务组件中手动控制 loading 效果。在业务组件中制作 loading 的状态,在发起请求时开启 loading 状态,请求结束的 finally 中关闭 loading 状态。

function Login() {
  const { message } = App.useApp()
  const [loading, setLoading] = useState(false)
  const onFinish = (values: LoginType.email) => {
    setLoading(true)
    api
      .login(values)
      .then(res => {
        const tmp = res as ResType
        localStorage.set('userToken', tmp.data.userToken)
        message.success('登录成功!')
      })
      .catch(err => {
        console.log('登录时出现异常:', err)
      })
      .finally(() => setLoading(false))
  }

同时可以将该状态应用到按钮上

<Button type='primary' htmlType='submit' block loading={loading}>登录</Button>