You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue Pinia中不使用await,基于响应式实现API顺序调用方案咨询

Vue响应式优化文件上传与POST请求实现

我目前在项目里用手动计数和回调处理文件上传,之后再发起最终POST请求,但团队前辈建议用Vue的响应式特性来优化。虽然可以用await,但我对响应式实现方式有点困惑,现有代码如下,求更优方案:

现有实现代码

App.vue

const isLoading = ref(0)

function Requester(url, method, data = null, callback = null) {
  // TODO: Remove primitive request counting technique and use reactive pattern
  isLoading.value++
  axios(url, { method: method, data: data })
    .then((response) => {
      if (callback) callback(response)
    })
    .catch((reason) => {
      alert(reason)
    })
    .finally(() => {
      isLoading.value--
    })
}

store.js

export const documentLibraryStore = defineStore('documentLibrary', () => {
  const useDocStore = doctore()
  const useGeneratedDocStore = generatedDocStore()
  const document = useGeneratedDocStore.newDocument
  const files = ref([])

  function isUploadDoneForAllFiles(uploadCount, extraFileCount) {
    return document.files.length - uploadCount == extraFileCount
  }

  function uploadPdfs() {
    let uploadCount = 0
    let extraFileCount = 0
    document.files.forEach((doc) => {
      if (!doc.id) { // id is undefind for non uploaded file
        add(doc.fileData, (value) => {
          uploadCount++
          doc.id = value.id
          // Here I am checking if files are uploaded or not
          if (isUploadDoneForAllFiles(uploadCount, extraFileCount)) {
            publish(document)
          }
        })
      } else {
        extraFileCount++
        if (extraFileCount == document.files.length) publish(document)
      }
    })
  }

  function formatPublishPayload() {
    document.files = document.files.map((file) => {
      return {
        id: file.id,
        fileId: file.fileId
      }
    })
  }
  // This should only called once we have id's for files uploaded
  function publish(documentset) {
    formatPublishPayload()
    if (document) {
      request('URL_TO_UPLOAD_DOCUMENT', 'POST', documentset, (response) => {
        useDocStore.appendDocumentSets(response.data)
        useGeneratedDocStore.clearNewDocumentSet()
      })
    }
  }

  function add(uploadFile, setID) {
    if (uploadFile) {
      const formData = new FormData()
      formData.append('file', uploadFile)
      request('URL_TO_UPLOAD_FILE', 'POST', formData, (value) => {
        files.value.push(value.data[0])
        // this is call back I am using to set id received from backend to set for uploaded files
        setID(value.data[0]) 
      })
    }
  }

  function publishDocumentSet() {
    uploadPdfs()
  }
  return { files, add, publishDocumentSet }
})

响应式优化方案

1. 重构Requester为异步函数,移除回调依赖

把Requester改成返回Promise的async函数,用await处理异步请求,同时保留loading计数逻辑:

// App.vue
const isLoading = ref(0)

async function Requester(url, method, data = null) {
  isLoading.value++
  try {
    const response = await axios(url, { method, data })
    return response
  } catch (reason) {
    alert(reason)
    throw reason // 抛出错误方便上层捕获处理
  } finally {
    isLoading.value--
  }
}

2. 基于响应式状态跟踪文件上传情况

不再手动维护计数变量,利用Vue响应式特性跟踪文件上传状态,用Promise.all批量处理上传请求:

// store.js
export const documentLibraryStore = defineStore('documentLibrary', () => {
  const useDocStore = doctore()
  const useGeneratedDocStore = generatedDocStore()
  const document = useGeneratedDocStore.newDocument
  const files = ref([])
  // 新增响应式变量防止重复上传
  const isUploading = ref(false)

  // 计算属性:自动判断所有文件是否已上传(都有id)
  const allFilesUploaded = computed(() => {
    return document.files.every(doc => !!doc.id)
  })

  // 监听上传完成状态,自动触发publish
  watch(allFilesUploaded, (isDone) => {
    if (isDone && !isUploading.value) {
      publish(document)
    }
  }, { immediate: true }) // 立即执行,处理已有文件全部上传的场景

  async function uploadPdfs() {
    if (isUploading.value) return // 避免重复触发上传
    isUploading.value = true

    // 批量发起未上传文件的请求
    const uploadPromises = document.files
      .filter(doc => !doc.id)
      .map(async (doc) => {
        const response = await add(doc.fileData)
        // 响应式更新文件id,触发计算属性变更
        doc.id = response.data[0].id
        doc.fileId = response.data[0].fileId
        return response
      })

    try {
      await Promise.all(uploadPromises)
    } catch (error) {
      console.error('文件上传失败:', error)
    } finally {
      isUploading.value = false
    }
  }

  function formatPublishPayload() {
    document.files = document.files.map((file) => {
      return {
        id: file.id,
        fileId: file.fileId
      }
    })
  }

  async function publish(documentset) {
    if (!documentset) return
    formatPublishPayload()
    try {
      const response = await Requester('URL_TO_UPLOAD_DOCUMENT', 'POST', documentset)
      useDocStore.appendDocumentSets(response.data)
      useGeneratedDocStore.clearNewDocumentSet()
    } catch (error) {
      console.error('发布请求失败:', error)
    }
  }

  async function add(uploadFile) {
    if (!uploadFile) return
    const formData = new FormData()
    formData.append('file', uploadFile)
    const response = await Requester('URL_TO_UPLOAD_FILE', 'POST', formData)
    files.value.push(response.data[0])
    return response
  }

  function publishDocumentSet() {
    uploadPdfs()
  }

  return { files, add, publishDocumentSet, allFilesUploaded, isUploading }
})

优化点说明

  • 移除回调与手动计数:用async/await替代回调避免嵌套,通过计算属性allFilesUploaded自动判断上传完成状态,无需手动维护计数变量。
  • 响应式自动触发逻辑:通过watch监听上传完成状态,符合Vue响应式设计思想,无需手动判断执行时机。
  • 防重复操作:新增isUploading变量避免重复发起上传请求,提升交互稳定性。
  • 错误处理更清晰:每个异步环节单独捕获错误,便于定位问题,替代全局alert提示更友好。

内容的提问来源于stack exchange,提问作者Nixit Ahir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 13:10:23