Cómo controlar la carga en Axios

Se puede decir que la visualización y cancelación de la carga es un problema que debe preocupar a todos los front-end a la hora de realizar la interfaz. Lo que este artículo le ayudará a resolver es cómo combinar axios para manejar de forma más concisa la lógica de carga, visualización y cancelación .

En primer lugar, cuando normalmente nos ocupamos de negocios, la carga generalmente se divide en tres tipos: carga de botón , carga local y carga global .

botón de carga

De hecho, la razón por la que quiero escribir este blog es también por la carga de este botón. La mayoría de las veces, cuando escribimos negocios de carga de botones, escribimos así.

const loading = ref(false)
try {
    loading.value = true
    const data = await axios.post(`/api/data`)
}
finally {
    loading.value = false
}
复制代码

O escrito así

const loading = ref(false)
loading.value = true
axios.post(`/api/data`)
    .then(data => {
        //do something
    })
    .finally(() => {
        loading.value = false
    })
复制代码

Puede ver que siempre tenemos que lidiar con el estado inicial y final de la carga. Y muchas interfaces deben escribirse de esta manera. Esto es demasiado engorroso, entonces, ¿podemos hacer esto?

const loading = ref(false)
const data = await axios.post(`/api/data`,{loading:loading})
复制代码

El estado de carga lo maneja de manera uniforme axios. ¿No es este código mucho más simple? El método de procesamiento también es muy sencillo.

// 请求拦截器
axios.interceptors.request.use(config = >{
    if (config.loading) {
      config.loading.value = true
    }
})
// 响应拦截器
axios.interceptors.response.use(
    response => {
        if (response.config.loading) {
            res.config.loading.value = false
        }
    },
    error => {
        if (error.config.loading) {
            config.loading.value = false
        }
    }
)
复制代码

Solo necesitamos cambiar el valor de carga en el interceptor axios. Tenga en cuenta que se debe pasar un valor de la clase ref. Esta forma de escritura solo es aplicable a vue3. vue2 no funciona.

En vue2 podemos pensar en escribirlo así.

<template>
    <a-button loading="loading.value">
        保存
    </a-button>
</template>

<script>
  export default {
    data () {
        return {
            loading: { value: false },
        }
    },
    mounted () {
        const data = await axios.post(`/api/data`,{loading:this.loading})
    },
  }
</script>
//拦截器和vue3写法一样
复制代码

Pero lamentablemente esto no funciona. Las razones son las siguientes

//接口调用
axios.post(接口地址,配置项)
//拦截器
axios.interceptors.request.use(配置项 => {})
复制代码

En axios, los elementos de configuración pasados ​​por nuestra llamada de interfaz y los elementos de configuración devueltos por el interceptor no tienen la misma dirección de memoria. axios realiza un procesamiento de copias profundo. Entonces, el objeto de carga pasado y el objeto de carga devuelto no son el mismo objeto. Entonces nuestras modificaciones en el interceptor son completamente inútiles.

Pero ¿por qué vue3 puede hacerlo? Debido a que el objeto devuelto por ref es una instancia de la clase RefImpl y no un objeto ordinario, axios no procesa dichos objetos de instancia cuando realiza copias profundas. Entonces podemos comenzar desde aquí para transformar nuestro método de escritura de axios. El código se muestra a continuación:

código axios:

const _axios = axios.create({
  method: `post`,
  baseURL: process.env.VUE_APP_BASE_URL,
})
//注意:拦截器中比vue3多了个loading!!!
// 请求拦截器
_axios.interceptors.request.use(config = >{
    if (config.loading) {
      config.loading.loading.value = true
    }
})
// 响应拦截器
_axios.interceptors.response.use(
    response => {
        if (response.config.loading) {
            res.config.loading.loading.value = false
        }
    },
    error => {
        if (error.config.loading) {
            config.loading.loading.value = false
        }
    }
)

export const post = (url, params, config) => { 
    if (config?.loading) {
        class Loading {
            loading = config.loading
        }
        config.loading = new Loading()
    }
    return _axios.post(url, params, config)
}
复制代码

Cómo utilizar:

<template>
    <a-button loading="loading.value">
        保存
    </a-button>
</template>

<script>
  import { post } from '@api/axios'
  export default {
    data () {
        return {
            //这里的loading可以取任意名字。但是里面必须有value
            loading: { value: false },
        }
    },
    mounted () {
        const data = await post(`/api/data`,{loading:this.loading})
    },
  }
</script>
复制代码

Se puede ver que el principio de implementación también es muy simple. Podemos simplemente convertir el objeto de carga en la configuración entrante en un objeto de instancia en axios. Registre el objeto que pasamos en el objeto de instancia, y también pensamos que aquí tendremos una carga más que el método de escritura vue3, para lograr capacidad de respuesta.

carga parcial

Hay dos formas de agregar carga parcial:

  1. Utilice directivas personalizadas para pasar verdadero y falso. Esta desventaja es que no es lo suficientemente flexible: es difícil agregar elementos dentro del componente localmente y solo se puede agregar como un componente completo. Cabe mencionar que para cambiar la lógica de verdadero y falso, podemos usar el método de carga del botón mencionado anteriormente. El método de implementación específico no se describirá aquí. Si es necesario, puede dejar un mensaje en el área de comentarios.

  2. Empaquetado en axios. Cada vez que se llama a la interfaz, se pasa el DOM que debe cargarse. Una vez completada la llamada a la interfaz, elimine el dom. He aquí cómo hacerlo.

Aquí hay un paquete de pila de tecnología vue3 + antdV3. Los ganchos se utilizan aquí para eliminar la lógica de configurar y eliminar la carga.

código axios:

const _axios = axios.create({
  method: `post`,
  baseURL: import.meta.env.VITE_BASE_URL,
})

const { setLoading, deleteLoading } = useAxiosConfig()
// 请求拦截器
_axios.interceptors.request.use(config = >{
    setLoading(config)
})
// 响应拦截器
_axios.interceptors.response.use(
    response => {
        deleteLoading(res.config)
    },
    error => {
        deleteLoading(res.config)
    }
)

export const post = (url, params, config) => { 
    return _axios.post(url, params, config)
}
复制代码

código de ganchos

import { createApp } from 'vue'
import QSpin from '@/components/qSpin/QSpin.vue'
import type { RequestConfig, AxiosError } from '@/types/services/http'
export default function () {
  /** 使用WeakMap类型的数据 键名所指向的对象可以被垃圾回收 避免dom对象的键名内存泄漏 */
  const loadingDom = new WeakMap()
  /**
   * 添加局部loading
   * @param config
   */
  const setLoading = (config: RequestConfig) => {
    const loadingTarget = config.dom
    if (loadingTarget === undefined) return
    const loadingDomInfo = loadingDom.get(loadingTarget)
    if (loadingDomInfo) {
      loadingDomInfo.count++
    } else {
      const appExample = createApp(QSpin)
      const loadingExample = appExample.mount(document.createElement(`div`)) as                 InstanceType<typeof QSpin>
      loadingTarget.appendChild(loadingExample.$el)
      loadingExample.show(loadingTarget)
      loadingDom.set(loadingTarget, {
        count: 1, //记录当前dom的loading次数
        appExample,
      })
    }
  }
  /**
   * 删除局部loading
   * @param config
   */
  const deleteLoading = (config: RequestConfig) => {
    const loadingTarget = config.dom
    if (loadingTarget === undefined) return
    const loadingDomInfo = loadingDom.get(loadingTarget)
    if (loadingDomInfo) {
      if (--loadingDomInfo.count === 0) {
        loadingDom.delete(loadingTarget)
        loadingDomInfo.appExample.unmount()
      }
    }
  }
 
  return { setLoading, deleteLoading }
}

复制代码

La lógica básica es muy simple. Solo necesita agregar la carga cuando se realiza la solicitud de la interfaz y eliminar la carga cuando se completa la respuesta de la interfaz. Pero hay un problema que viene con esto: si varias interfaces solicitan al mismo tiempo o una interfaz solicita con frecuencia, todas deben cubrir el mismo DOM, por lo que la carga que agreguemos tendrá muchas idénticas, cubriéndose entre sí. Por lo tanto, el código anterior define un loadingDom para registrar qué DOM se está cargando actualmente. Si ingresa el mismo, el recuento aumentará en uno y, una vez finalizado, el recuento disminuirá en uno. Si el recuento es cero, se elimina la carga.

Utilice código de ejemplo:

<template>
  <div>
    <div ref="head_dom">我是头部数据</div>
    <a-card ref="card_dom">我是卡片内容</a-card>
  </div>
</template>

<script setup lang="ts">
  import { post } from '@api/axios'
  import { ref, onMounted } from 'vue'
  const head_dom = ref()
  const card_dom = ref()
  //这边写了两个是为了演示下 直接在html标签上面绑定ref拿到的就是dom。在组件上面拿到的是组件实例要$el一下
  onMounted(async () => {
    const data1 = await post(`/api/head`, { dom: head_dom.value })
    const data2 = await post(`/api/card`, { dom: card_dom.value.$el })
  })
</script>
复制代码

La siguiente es una breve explicación del código del componente QSpin en el código de gancho.

<template>
  <div v-show="visible" class="q-spin">
    <spin tip="加载中" />
  </div>
</template>

<script setup lang="ts">
  import { Spin } from 'ant-design-vue'
  import { ref } from 'vue'

  const visible = ref(false)
  const show = (dom: HTMLElement) => {
    visible.value = true
    dom.style.transform = dom.style.transform || `translate(0)`
  }
  defineExpose({ show })
</script>

<style scoped lang="less">
  .q-spin {
    position: fixed;
    z-index: 999;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: rgb(0 0 0 / 10%);
  }
</style>
复制代码

Aquí hay una encapsulación secundaria simple del componente Spin de antdv3. La explicación principal es un método de carga que sobrescribe el dom entrante.

El método utilizado en la mayoría de los lugares es una combinación de posicionamiento relativo y absoluto, pero aquí se utiliza una combinación de transformación y posicionamiento fijo. Porque puede haber tal situación en nuestro proyecto.

  <div style="position: relative">
    <div ref="div_dom">
      <div style="position: absolute">我是内容</div>
    </div>
  </div>
复制代码

Si queremos agregar carga al div medio, use una combinación de posicionamiento relativo y absoluto. Luego, el div del medio agregará una posición: atributo relativo a la hoja de estilo, por lo que el código quedará así

 <div style="position: relative">
    <div style="position: relative" ref="div_dom">
      <div style="position: absolute">我是内容</div>
    </div>
  </div>
复制代码

Obviamente, el nodo raíz de nuestro posicionamiento div de tercera capa ha cambiado de la primera capa a la segunda capa, lo que puede causar confusión en nuestros estilos. Por lo tanto, el autor utiliza una combinación de transformación y posicionamiento fijo. Aunque la situación anterior aún puede ocurrir, la posibilidad de que ocurra se reducirá considerablemente.

carga global

Esto es muy simple. Si ha encapsulado la carga local, puede pasar directamente document.body en el dom del elemento de configuración.

Supongo que te gusta

Origin blog.csdn.net/weixin_53474595/article/details/130238972
Recomendado
Clasificación