【闭包】vant上传组件uploader的自定义参数实现源码分析

1,337 阅读2分钟

背景

最近在写一个投票项目,涉及到头像图片的修改上传。原本想用阿里云esc做一个base64格式的简单存储,但实际接口响应速度太慢(图片资源十几mb,带宽有限),后来还是购买了oss做文件存储,走formdata形式存图片。

问题

使用vant的van-uploader 组件,但after-read 的事件回调,默认只接收一个参数 file。 动态渲染list之后,要在 v-for 循环中使用它,还需要额外的参数(例如 item 和 index),这样就能直接在回调里,把文件读取结果绑定到item中,直接给后续的单项修改用。

思路

从vant的官网调用示例看,after-read用匿名函数接收,只有入参file,即上传后读取的文件对象。 查看uploader源码如下:

 methods: {
       ...
onAfterRead: function onAfterRead(files, oversize) {
        ...
        if (this.afterRead) {
          this.afterRead(validFiles, this.getDetail());
        }
    },
       ...
   }
 props: {
    ...
    afterRead: Function,
    ...
    }

分析

组件的调用逻辑

源码可以看出,uploader组件,是在文件选择读取后,将文件信息封装为validFiles,然后从prop中读取是否有设置函数回调,有则将validFiles做为形参传入回调函数。(另外还有一个形参,送一些组装的信息(名称、下标))。

所以正常使用组件时,默认通过设置props的afterRead,去注册一个回调函数A,上传文件后,会触发组件内置的onAfterRead钩子函数,函数中又会调用注册的回调函数A,并将文件信息以参数形式传入

用闭包来定制送参流

要基于组件的现有机制逻辑,在回调函数A中,传入列表的item对象。 这时,就要用到闭包

    afterRead(item) {
      let vm = this;
      return file => {
        //这里将file对象的file属性(文件信息)添加到item的fileData属性
        item.fileData = file.file;
        //函数中new FormData(),将fileData,用key-value形式(key由接口指定)append到  FormData对象里,如formData.append("file", item.fileData) ,送去接口。这里不贴接口调用代码了
        vm.uploadF(item.fileData);
      };
    },

闭包的形式,会return出一个新函数,给到prop的注册函数。新函数中,会读取作用域外的item,并进行属性赋值操作和函数调用等。 这样就实现了想要的效果。

赶项目时,想简单按vant官网来走上传,结果发现需要额外参数,不然后面的流程,在数据整理上就会很复杂,需要额外变量来记录上传列表项;着急时也没多想,先去问了chatgpt,也是很快给出了方案,一看原来是闭包。

贴上copilot的回答

微信图片_20240709164418.png

总结

之前对闭包的用法,主要集中在防抖节流或者写公共类。在这种组件应用场景中,会不自觉盯着组件官网的现有api。如果不能直接实现,也不太能自觉绕出来。还好有chatgpt,在打开思路上起了很多作用。