小程序上传进度条,还要自己监听 onProgressUpdate 吗?

你小程序里多半有一段长得像这样的上传代码:

javascript 复制代码
// pages/feedback/index.vue
export default {
  data() {
    return { uploading: false, percent: 0, uploadTask: null };
  },
  methods: {
    handleUpload() {
      uni.chooseImage({
        count: 1,
        success: res => {
          this.uploading = true;
          const task = uni.uploadFile({
            url: 'https://api.example.com/uploadImg',
            filePath: res.tempFilePaths[0],
            name: 'file',
            formData: { from: 'feedback' },
            success: () => { this.uploading = false; },
            fail: () => {
              this.uploading = false;
              uni.showToast({ title: '上传失败', icon: 'none' });
            }
          });
          task.onProgressUpdate(e => { this.percent = e.progress; });
          this.uploadTask = task; // 存起来,取消时才用得到
        }
      });
    },
    handleCancel() {
      this.uploadTask && this.uploadTask.abort();
      this.uploading = false;
      this.percent = 0;
    }
  }
};

它能跑,也上线了。但这段代码有三处不对劲:

  1. 状态是手工账。 uploadingpercentuploadTask 三个字段,得在 success / fail / 取消三条路径里挨个复位。哪天加个"失败自动清进度",漏改一条路径,进度条就永远停在 73%。
  2. 取消靠自觉。 uploadTask 引用得自己存,取消得自己调 abort(),复位得自己写。页面一销毁忘了取消,请求还在后台跑。
  3. 没法复用。 头像、意见反馈、工单附件,每个页面把五十行复制一遍改个 url。三个月后想统一加登录态过期处理,得找出所有副本挨个改。

用户那头的体感更直接:点了上传没进度,以为卡死,再点一次,服务器收到两张一样的图。

理想的上传该长什么样

把需求摊开,其实就四条:

  • 进度、加载态是响应式状态,不是手工变量,任何路径退出都自动复位;
  • 取消是内置动作,不用自己攒 task 引用;
  • 重传就是再调一次,不用把 chooseImage 到 uploadFile 整条链重走;
  • 写法和普通请求一致,上传不该是一套独立于 GET/POST 之外的方言。

第 4 条最容易被忽略,但它决定三个月后的维护成本:分页一套写法、上传又一套写法,团队每个人都在维护好几套私房封装。

顺着这份清单找,会发现这正是请求策略库在做的事。alova 的定位是一套请求 API 跑在 Web、小程序/App(uni-app/Taro)、服务端,上传只是其中一个策略场景。

换成 alova 写一遍

先装包(uni-app 适配器,注意只支持 vue3 版 uni-app):

bash 复制代码
npm install alova @alova/adapter-uniapp @alova/shared --save

建实例,请求/存储适配器一次配齐:

javascript 复制代码
// api/index.js
import { createAlova } from 'alova';
import AdapterUniapp from '@alova/adapter-uniapp';

export const alovaInst = createAlova({
  baseURL: 'https://api.example.com',
  ...AdapterUniapp(),
  responded(response) {
    const { statusCode, data } = response;
    if (statusCode >= 400) throw new Error('请求错误');
    return data || null;
  }
});

上传本身,在 method 的 config 里标 requestType: 'upload',适配器内部就改调 uni.uploadFiledata 里的 namefilePath 传给 uploadFile,其余字段自动进 formData

html 复制代码
<template>
  <button @click="handleImageChoose">上传图片</button>
  <button v-if="loading" @click="abort">取消</button>
  <progress v-if="loading" :percent="percent" show-info />
</template>

<script setup>
import { computed } from 'vue';
import { useRequest } from 'alova/client';
import { alovaInst } from '@/api';

const uploadFile = (name, filePath, formData) =>
  alovaInst.Post(
    '/uploadImg',
    { name, filePath, ...formData }, // 额外字段自动进 formData
    { requestType: 'upload', fileType: 'image' }
  );

const { loading, uploading, send, abort } = useRequest(uploadFile, {
  immediate: false
});

// uploading = { loaded, total },响应式,直接算百分比
const percent = computed(() =>
  uploading.value.total > 0
    ? Math.floor((uploading.value.loaded / uploading.value.total) * 100)
    : 0
);

const handleImageChoose = () => {
  uni.chooseImage({
    count: 1,
    success: res => send('file', res.tempFilePaths[0], { from: 'feedback' })
  });
};
</script>

对照开头那三条清单:

  • 进度:uploading.loaded / uploading.total,响应式,没有任何一行 onProgressUpdate
  • 加载态:loading 自动起落,失败、取消都会复位,不存在"进度条卡在 73%";
  • 取消:解构出来的 abort() 直接绑按钮,task 引用的存取是库内部的事;
  • 重传:失败后再点按钮,send() 再走一次就是重传,参数还能换。

dataloading 这些和普通请求完全同名同形,这就是清单第 4 条:上传不再是方言。团队里会写 useRequest(getList) 的人,看这段代码零学习成本。

顺带一提,如果你项目已经在用 axios,它不冲突:axios 可以作为 alova 的请求适配器继续发请求(@alova/adapter-axios),axios 负责发请求,alova 负责怎么请求。不过小程序环境用不到 axios,这里按下不表。

多文件、批量怎么办

单文件够用就到此为止。要做"九宫格传图 + 每张独立进度 + 失败单张重传",alova@3.3+ 还有个 useUploader:base64 / File / Blob / ArrayBuffer 直接丢进去,每个文件独立的 status 和 progress,upload(1, 3) 指定重传,mode: 'batch' 批量提交。要说明的是:它默认的弹窗选文件只在浏览器里有效,小程序/uni-app 环境需要通过 useUploader.selectFile 自定义选择逻辑。单文件场景用上面的 useRequest 写法就好,不必上它。


alova 是一套请求 API,跑在 Web、小程序/App(uni-app/Taro)和服务端(BFF)。上传只是入口,分页竞态、token 并发刷新、请求缓存这些"每个项目都要再写一遍"的东西,才是请求策略层真正省心的地方。

相关推荐
慧都小项2 分钟前
夜间Web回归跑到一半停了?TestComplete 15.83提升批量测试稳定性
前端·测试工具·数据挖掘·回归·汽车·web
两只羊ovo5 分钟前
Agent ≠ 聊天机器人:从一问一答到 ReAct 循环,到底差在哪?
前端
摇曳的精灵13 分钟前
前端缓存优化
前端·缓存·缓存优化
老王以为17 分钟前
走进 AI Agent 第二篇:决定 AI Agent 能力上限的关键技术
前端·人工智能·机器学习
何以解忧,唯有..23 分钟前
Python 异常处理完全指南:从基础到高级实践
开发语言·前端·python
Buke..34 分钟前
【小程序逆向】某游快爆 AI 逆向 sign参数:AI 辅助分析与 MCP 工具链实战
前端·人工智能·爬虫·python·小程序·notepad++
ly76892 小时前
CSS 从入门到进阶:系统掌握现代网页样式与布局
前端·css
SWAGGY..2 小时前
【C++初阶】:(15)模板进阶--从非类型参数到模板特化与分离编译
java·服务器·前端
YHHLAI2 小时前
从「跑分」到「干活」:Benchmark 与 GPT 5.6 的两种叙事
大数据·前端·人工智能·react.js·前端框架
BigTopOne2 小时前
ArLiveLite 用到的 C++/JNI 技术点
前端