el-upload上传组件的学习和使用

发布时间:2023-08-12浏览次数:2177 次
关于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的钩子。

基本上到这一步,我们需要的效果就算是完成了,实际上,我们的代码没有用到任何的钩子,可以说官方提供了那么多钩子,在一定程度上,对我们产生了干扰。最后给大家看一下,我实现的上传效果。

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

扫一扫,在手机上查看