前言
大家好,我叫竹业,今天来深入了解一下el-upload组件的用法。
组件使用
首先来看下组件的使用
- autoUpload为false时,可以通过submit方法来触发
<template>
<el-upload
class="upload-demo"
action="https://jsonplaceholder.typicode.com/posts/"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
multiple
:limit="3"
:on-exceed="handleExceed"
:file-list="fileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>
</template>
<script>
export default {
data() {
return {
fileList: [
{
name: "food.jpeg",
url: "https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100",
},
{
name: "food2.jpeg",
url: "https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100",
},
],
};
},
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
},
handleExceed(files, fileList) {
this.$message.warning(
`当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${
files.length + fileList.length
} 个文件`
);
},
beforeRemove(file, fileList) {
return this.$confirm(`确定移除 ${file.name}?`);
},
},
};
</script>
源码实现
upload组件
实现上传的组件是upload组件
- 1.点击整个upload组件,触发handleClick方法,js触发input的点击
- 2.input的handleChange方法,拿到文件后,将数据初始化,调用post方法进行文件上传
- 3.上传文件之前,会执行beforeUpload钩子函数,返回false,则停止请求
- 4.post方法里的this.httpRequest(options)可以自己定义,默认有一个上传的方法,单独写在ajax.js
<template>
<div tabindex="0" :class="['el-upload', `el-upload--${listType}`]" @click="handleClick">
<upload-dragger v-if="drag" @file="uploadFiles" :disabled="disabled"></upload-dragger>
<slot v-else></slot>
<input class="el-upload__input" type="file" ref="input" :name="name" @change="handleChange" :multiple="multiple" :accept="accept" />
</div>
</template>
<script>
export default {
methods: {
handleClick() {
if (!this.disabled) {
this.$refs.input.value = null;
this.$refs.input.click();
}
},
handleChange(ev) {
const files = ev.target.files;
if (!files) return;
this.uploadFiles(files);
},
uploadFiles(files) {
// ...
// 对文件进行数据格式化,并自动上传
postFiles.forEach(rawFile => {
// 文件数据进行处理
this.handleStart(rawFile);
if (this.autoUpload) this.upload(rawFile);
});
},
upload(rawFile) {
this.$refs.input.value = null;
// 没有beforeUpload钩子函数,直接执行上传文件接口
if (!this.beforeUpload) {
return this.post(rawFile);
}
const before = this.beforeUpload(rawFile);
if (before !== false) {
this.post(rawFile);
} else {
this.onRemove(null, rawFile);
}
},
// 上传文件
post(rawFile) {
const { uid } = rawFile;
const options = {
headers: this.headers,
withCredentials: this.withCredentials,
file: rawFile,
data: this.data,
filename: this.name,
action: this.action,
onProgress: e => {
this.onProgress(e, rawFile);
},
onSuccess: res => {
this.onSuccess(res, rawFile);
delete this.reqs[uid];
},
onError: err => {
this.onError(err, rawFile);
delete this.reqs[uid];
}
};
// httpRequest方法默认内部实现的上传方法,也可以自定义方法
const req = this.httpRequest(options);
this.reqs[uid] = req;
// 返回的req也可以支持promise
if (req && req.then) {
req.then(options.onSuccess, options.onError);
}
},
// 中断请求
abort(file) {
const { reqs } = this;
if (file) {
let uid = file;
if (file.uid) uid = file.uid;
if (reqs[uid]) {
reqs[uid].abort();
}
} else {
// 不传参数,中断所有xhr请求
Object.keys(reqs).forEach((uid) => {
if (reqs[uid]) reqs[uid].abort();
delete reqs[uid];
});
}
},
handleStart(rawFile) {
rawFile.uid = Date.now() + this.tempIndex++;
let file = {
status: 'ready',
name: rawFile.name,
size: rawFile.size,
percentage: 0,
uid: rawFile.uid,
raw: rawFile
};
this.uploadFiles.push(file);
this.onChange(file, this.uploadFiles); // 触发传入的onChange函数
}
}
}
</script>
httpRequest默认方法
- 1.首先new XMLHttpRequest
- 2.注册上传的进度回调函数onprogress
- 3.传入的data参数,添加到formData里,传递给后台
- 4.onerror、onload事件触发成功或失败的回调
- 4.传入的headers参数,添加到请求头里
- 5.传入了withCredentials参数为true,设置withCredentials为true
- 6.xhr.send(formData)发送请求
function upload(option) {
const xhr = new XMLHttpRequest();
// 请求的url
const action = option.action;
// 上传的进度
if (xhr.upload) {
xhr.upload.onprogress = function progress(e) {
if (e.total > 0) {
e.percent = e.loaded / e.total * 100;
}
option.onProgress(e);
};
}
const formData = new FormData();
// formData的参数
if (option.data) {
Object.keys(option.data).forEach(key => {
formData.append(key, option.data[key]);
});
}
// 默认将文件流添加到formData
formData.append(option.filename, option.file, option.file.name);
// 错误的回调
xhr.onerror = function error(e) {
option.onError(e);
};
// 上传成功的回调
xhr.onload = function onload() {
if (xhr.status < 200 || xhr.status >= 300) {
return option.onError(getError(action, option, xhr));
}
option.onSuccess(getBody(xhr));
};
xhr.open('post', action, true);
// 不同源是够携带cookie
if (option.withCredentials && 'withCredentials' in xhr) {
xhr.withCredentials = true;
}
// 添加请求头
const headers = option.headers || {};
for (let item in headers) {
if (headers.hasOwnProperty(item) && headers[item] !== null) {
xhr.setRequestHeader(item, headers[item]);
}
}
xhr.send(formData);
return xhr;
}
最后
这里主要分析了upload.vue组件和ajax.js的httpRequest方法,这个组件还是实现很简单的,最后祝大家周末愉快~