el-upload上传组件的学习和使用
关于el-upload的使用,其实相信大多数人都多多少少踩过坑,当然如果不求代码质量,使用官方提供样式的应该很容易完成。但实际情况是,大多时候我们需要根据自己的业务场景自定义上传的样式和效果。
事实上,element-plus官方提供的属性和api是相当丰富的,但是问题也正出在这里,正是因为有超多的属性配置,而且这些配置之间存在一些相互之间的影响,因此,el-upload说简单也简单,说难也难。
使用el-upload写了很多次上传了,但是每次写这玩意还是略显头疼,每次写之前还是得参考文档,因此,今天打算系统性的对el-upload做以总结。
一、基础
在学习el-upload上传之前,我们需要先了解,el-upload分为自动上传和手动上传两种,自动上传即选取文件之后立即执行上传操作。手动上传则是先选取文件,稍后再通过点击其他案例的方式触发上传。
el-upload的基本属性
最简单的el-upload写法如下:
<el-upload action="#" name="image">
<el-button>选择并上传</el-button>
</el-upload>
以上为一个自动上传(默认)例子,在这个例子中,我们使用到了action属性和name属性:
action属性:上传的服务器接口
name属性:服务器端接收文件时的字段名称
在自动上传时,这两个属性都是必填的。el-upload默认的上传方式为自动上传。如果我们想实现手动上传,则需先关闭自动上传,并且定义一个上传的回调。写法如下:
<template>
<el-upload ref="uploadRef" :auto-upload="false" :http-request="uploadRequest">
<el-button>选择</el-button>
</el-upload>
<el-button @click="uploadRef.submit()">上传</el-button>
</template>
<script setup>
const uploadRef = ref()
const uploadRequest = ()=>{
console.log('上传请求')
}
</script>
注意,手动上传时,我们无需再定义action和name属性,虽然官方文档中提示这两个属性是必需的,但是因为我们采取了手动上传,这些数据后期都会写在上传请求中。上面的代码示例涉及到三个属性和一个方法,分别如下:
auto-upload属性:是否为自动上传,默认为true,手动上传时,我们需要声明关闭
http-request属性:上传请求方法,参数为一个回调函数。
在上面的案例中,我们将上传图片的按钮放置在了el-upload的外部(也可以放置在el-upload的内部),然后点击按钮的时候,通过触发el-upload实例身上的submit()方法来达到触发提交的事件。
需要注意的地方还有:
1、需要添加一个el-upload的实例引用,即:uploadRef。后期通过该实例引用,发起上传或取消上传等事件。
2、之所以直接将触发上传的方法写在模板中,是为了避免过多的方法名形成干扰。
3、如果将触发按钮放置在el-upload内时,选取图片的组件需要写在触发文件选择的trigger插槽中(在没有使用trigger插槽时,点击el-upload标签内的元素都会触发文件选择,当使用了trigger插槽后,则trigger插槽外的元素不会触发文件选择)。
即,也可以使用下面的写法:
<template>
<el-upload ref="uploadRef" :auto-upload="false" :http-request="uploadRequest">
<template #trigger>
<el-button>选择</el-button>
</template>
<el-button @click="uploadStart">上传</el-button>
</el-upload>
</template>
<script setup>
const uploadRef = ref()
const uploadStart = ()=>{
uploadRef.value.submit()
}
const uploadRequest = ()=>{
console.log('上传请求')
}
</script>
一般情况下,我们选择第一种写法,相对代码会比较整洁一些。
二、文件上传相关配置:
multiple属性:是否支持多选(默认为false)。
limit属性:允许上传文件的最大数量。
file-list属性:默认上传文件,参数为一个数组对象,该对象由el-upload进行维护(当然,也可以自行维护,但是问题比较多)所以一般我们都选择一起维护,因此这里我们使用了v-model双向绑定的方式。
list-type属性:文件列表类型,即,选取文件之后,el-upload下方出现的已选择/上传文件列表。默认的为text,可选的有:picture和picture-card。样式分别如下:

其实,官方提供的三种list-type都不是我想要的,更多的时候,我还是希望可以自定义样式。因此,我们需要再了解一个属性:show-file-list,用来显示或隐藏上传文件列表。(也因此,一般我们在配置show-file-list属性的时候,是不需要配置list-type属性的,当然,除了你想使用 list-type='picture-card'的上传样式时,需要同时配置)。
截止目前,我们的代码如下:
<template>
<el-upload ref="uploadRef" :auto-upload="false" :http-request="uploadRequest"
v-model:file-list="files"
:show-file-list="false"
multiple :limit="5">
<template #trigger>
<el-button size="small" type="primary">选择</el-button>
</template>
<el-button @click="uploadRef.submit()" size="small" type="success" style="margin-left: 20px">上传</el-button>
</el-upload>
</template>
<script setup>
const uploadRef = ref() // el-upload实例引用
const uploadRequest = ()=>{
console.log('上传请求')
}
const files = reactive([]) // 上传文件
</script>
接下来,我们开始学习几个el-upload的生命周期钩子。
三、学习el-upload的生命周期钩子之前的准备:
在学习声el-upload的生命周期钩子之前,我们需要再加一个可以随时打印上传文件的 debug-files 调试按钮(也可以打开show-file-list),并且配置好我们的上传方法。
调试按钮:
<el-button @click="console.log(files)">debug:打印上传文件</el-button>
上传方法配置:
const uploadRequest = async (options)=>{
const formData = new FormData()
formData.append('image',options['file']);
const response = await axios.post('/upload',formData);
console.log(response)
}
这里的上传方法配置,以及里面使用到的上传接口,需要根据自己使用请求类库和后端语言自行配置,我这里使用的是已经提前封装好的axios库。
四、学习el-upload上传组件的生命周期钩子
4.1 监视上传列表数据变动 on-change钩子
在上面的示例中,我们对file-list做了数据双向绑定,我们希望在每次上传列表发生变动的时候,打印files。因此按照一般思路,我们写一个files的监视器:
watch(files,(newVal)=>{
console.log('上传列表数据files发生改变了')
},{deep:true})
理论上,只要files的数据发生改变,我们都能随时监视到,但实际测试的时候,发现vue3提供的监视方法在监视file-list时无效,虽然我们此处还是用了深度监视。但是通过点击我们提前准备好的 debug-files 按钮,我们发现files数据确确实实是改变了。此时,我们就需要了解第一个生命周期钩子:on-change钩子:文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用。
配置on-change属性:
:on-change="handleChange"
对应的handleChange方法:
const handleChange = (uploadFile,uploadFiles)=>{
console.log(uploadFiles)
console.log(files)
console.log(uploadFiles === files) // false
}
在这个钩子里,我们可以在上传列表发生变动时,随时打印。但是这里发现一个问题,此时我们双向绑定的files数据,此时与其内部维护的上传列表uploadFiles数据并未同步,handleChange执行完之后,我们再打印debug-files按钮,此时的files才与uploadFiles数据一致。也就是说,el-upload的数据不是在这一步同步的。
另一个问题:
随便上传几张图片,进行测试,发现一个问题:
当list-type默认为text时,已上传文件列表files里没有url属性,当list-type为picture或者 picture-card时,则存在url属性。
此时,我们可以通过URL.createObjectURL 为其动态生成,代码如下:
const handleChange = (uploadFile,uploadFiles)=>{
console.log(uploadFiles)
uploadFiles.map(item=>{
item.url = URL.createObjectURL(item.raw)
})
}
当然,我们也可以通过设置list-type="picture"的方式,来让el-upload自行生成url。这里使用这种方式,主要是为了调试。
接下来,按照我们一般的需求,我们需要准备一个图片预览的容器。来显示我们上传的图片(使用官方提供的上传列表时则不用)。代码如下:
<h4>图片预览区</h4>
<ul class="viewBox">
<li v-for="(file,index) of files" :key="index"><el-image :src="file.url" fit="cover"></el-image></li>
</ul>
对应的样式如下:
<style lang="scss" scoped>
.viewBox{
width: 100%;
li{
width: 180px; height: 120px; border: 1px solid #e1ecf9; padding: 3px; margin: 3px; float: left;
.el-image{
width: 100%; height: 100%; display: block;
}
}
}
</style>
效果如下:

我们想要实现的效果是,在图片选择之后且未上传之前,就立即将本地图片显示到预览区,待图片上传完成后,再用服务器返回的地址进行替换。那么我们就需要在上传请求完成之后,通过uid匹配,然后用服务器返回的url地址,去替换掉原来的本地Blob地址,代码如下:
const uploadRequest = async (options)=>{
const formData = new FormData()
formData.append('image',options['file']);
const response = await axios.post('/upload',formData,{
params:{
process: {
water:true,
size:[180,160]
}
}
})
if (response && response.code === 0){
files.value.map(file=>{
if (file.uid === options.file.uid){
file.url= baseUrl + response.data.url;
}
})
}
}
这里需要注意一点就是,我们之前在on-change的钩子里,写了生成url的方法,在请求完成之后,该钩子还会被调用一次,因此,我们需要在对on-change钩子做进一步的处理,比如判断是否存在url,不存在的情况下,再去替换,或者直接去掉on-change的钩子。
基本上到这一步,我们需要的效果就算是完成了,实际上,我们的代码没有用到任何的钩子,可以说官方提供了那么多钩子,在一定程度上,对我们产生了干扰。最后给大家看一下,我实现的上传效果。

我这边加了图片排序和删除的功能,大家可以根据自己的需求,添加更多的效果。

CSS选择器:nth-child()的灵活用法及常见场景示例
CSS选择器中的nth-child()相信大家都比较熟悉,nth-child()和nth-of-type()的区别是:nth-child()不区分类型。大家经常
响应式网站布局的优缺点分析
响应式网站,通俗的讲就是使用CSS媒体查询技术,写一套代码,可以在多个终端上使用。在此之前,前端切图工作者是需要分别针对电脑端、移动端和平板等常见设备专门去写一
切图和前端什么关系?为什么前端又被称为切图仔?
很多初从事前端切图这个行业的新人,大多都有一个疑问?为什么要把前端开发人员称为切图仔呢?提起这个问题,我们还要从前端切图行业的发展历程来说起,当然,这个问题对与
为什么你的logo图模糊不清,移动端图片虚化的解决方案
在传统pc时代的时候,几乎99%的网站都在使用图片格式作为logo,而大部分网站都使用了png透明底的图片来做网站的logo。但是进入移动时代之后,这种习惯还是
目前主流的前端框架有哪些?
基本上每种语言都有对应的一些快速框架用于提升开发人员的效率,所谓框架更像是工具箱或者脚手架,在开发时如果能很好的利用框架可以起到事半功倍的效果。简单形象的说,如
移动端网站适配响应式的几种解决方案浅谈
在移动互联网时代,几乎移动端已是所有网站的标配。但是在解决移动端与pc端时,有了很多种解决方案。每种解决方案都有各自的优劣之处,我们从实现难度和SEO权重积累等