小工具知识网

当前位置: 首页 > 行业知识 > 【axios】你的进度条准确吗

行业知识

【axios】你的进度条准确吗

2026-11-05 14:36:26 来源:互联网转载

1、axios监听进度

上传和下载操作在前端中是非常常见的,当我们想知道上传或下载的进度时也不难,axios已经实现了监听进度的方法

import axios from 'axios'// 上传请求axios.post('/api/v1/upload', {    data: xxx  },  {    // onUploadProgress回调可以获取进度    onUploadProgress(e) {      const complete = e.loaded / e.total * 100    }})// 或者axios({  method: 'POST',  data: {    data: xxx  },  onUploadProgress(e) {    const complete = e.loaded / e.total * 100  }})

翻一下axios的源码,看看它是如何实现的

在/lib/adapters/xhr.js文件中,可以看到这么一段代码

let request = new XMLHttpRequest()// Not all browsers support upload eventsif (typeof config.onUploadProgress === 'function' && request.upload) {  request.upload.addEventListener('progress', progressEventReducer(config.onUploadProgress));}

axios是基于XMLHttpRequest来实现的

其中,config就是我们传给的axios的参数,如果是上传操作并且有传递onUploadProgress函数的话

就监听XMLHttpRequest的progress事件,然后周期性地触发回调函数progressEventReducer

再看一下progressEventReducer的实现:

function progressEventReducer(listener, isDownloadStream) {  return e => {    const loaded = e.loaded;    const total = e.lengthComputable ? e.total : undefined;    ...    const data = {      loaded,      total,      progress: total ? (loaded / total) : undefined,      ...    };    data[isDownloadStream ? 'download' : 'upload'] = true;  // 区别上传或是下载    listener(data);  };}

在progressEventReducer中,就会获取

  • loaded:已上传的buffer数据流
  • total:总的buffer数据流

    最终会将数据传给listener,即一开始传给axios的onUploadProgress回调,所以我们可以通过e.loaded / e.total来获取进度

    2、不准确的进度条

    获取进度条很简单,但是在实际使用中还是碰到了问题

    - 计算出来的进度与实际上传进度不符合

    - 可以看出:进度条已经走完,但是接口一直在pending中

    这个问题还是比较严重的,明明显示完成了上传,但是文件就是没有出来

    而且文件越大,差异就越明显

    原因

    造成这个原因,其实跟TCP协议发送数据的方式有关

    • 在客户端,会有一个send buffer,即数据缓存区
    • 这个buffer缓存区存储的就是等待发送的数据,tcp协议层会在适当的时候选取一部分数据发送出去
    • 当我们上传文件时,数据会被不断地write到这个缓存区;每次写入时就会被侦听到,然后调用一次onUploadProgress,e.loaded其实表示的就是写入到buffer缓存区的数据
    • 但是此时的数据可能还没有被发送出去,仍然在缓存区中放着;加上发送数据也需要时间
    • 等到数据全部到达Server的时候,才会执行response回调,这时候上传操作才算完成;但客户端那边早就显示发送完毕了,时差也就出来了

      3、模拟进度条

      如果解决这个问题呢?有一个比较保守的做法:用一个亦真亦假的进度条

      在进度达到一定值的时候,开始人为干涉;每次让它走一点,给人一种一直在上传的感觉,直到上传完成,赋值为100即可

      这里就使用vue3的方式实现一下:

      import { ref } from 'vue'/** * 模拟进度条 * @returns */export const useProgress = (name: string) => {  const progress = ref(0)  const timer = ref(null)  const onUploadProgress = (e: ProgressEvent) => {    const complete = (e.loaded / e.total) * 100    // 超过80 开始干涉    if (complete >= 80) {      if (timer.value) return      timer.value = window.setInterval(() => {        progress.value += (100 - progress.value) * 0.2  // 每次增加剩下的20%                // 超过99 不再变化(此时也接近上传完成了)        if (progress.value > 99) {          timer.value && window.clearInterval(timer.value)        }, 1000)      } else {      // 在80之前都按照axios计算出的进度来显示      progress.value = complete    }}    return {    progress,    onUploadProgress  }}

      可以在请求完成的时候,将progress设置为100即可

      具体的阈值可以根据情况自行设置

      其实,大名鼎鼎的NProgress库也是这么做的,我们平时使用它的姿势是这样的:

      NProgress.start()  // 开启进度条NProgress.done()  // 进度条结束

      可是,又没有监听接口,仅仅只是开启进度条,它怎么能预测我们的进度呢?

      答案是:它也是模拟进度条

      在其源码中,有这么一段代码:

      当我们没有传amount的时候,会自动根据当前进度条的status调整每次的进度amount

      当进度staus超过0.994的时候,就会一直停在0.994这个状态

      直到我们执行NProgress.done()时,会直接调用NProgress.set(1),进度条走完

      参考:

      https://github.com/axios/axios

      https://github.com/rstacruz/nprogress

上一篇:怎么解除人工智能服务器?

下一篇:Eureka切换Nacos时发现两个注册中心的解决方法