element-ui源码研究-upload组件

时间:2022-12-13 08:57:47

1. 文件上传按钮

为什么element-ui的上传如此好看

element-ui源码研究-upload组件

而我们的文件上传按钮却长得如此丑陋

element-ui源码研究-upload组件

同时,element-ui的upload组件,钩子函数太多,可能功能上过去冗余,对于初学者不是很友好

element-ui源码研究-upload组件

看到这么多的钩子函数,可能内心是一脸的惆怅,那如何实现一个自己的文件上传组件呢?

2.如何实现一个自己的上传按钮组件

2.1 element-ui upload组件源码所在位置

element-ui源码研究-upload组件

我们先核心关注upload.vue中的源码

element-ui源码研究-upload组件

2.2实现原理

1.界面上放一个普通的button按钮,再放一个file表单元素(真正用来上传文件用的),同时对button按钮进行美化,对file表单元素进行隐藏

2.点击button按钮,去触发file的click事件,以此触发file元素的change事件,类似于隔山打牛的效果,element-ui upload源码如下:

render(h) {
let {
handleClick,
drag,
name,
handleChange,
multiple,
accept,
listType,
uploadFiles,
disabled,
handleKeydown
} = this;
const data = {
class: {
'el-upload': true
},
on: {
click: handleClick,
keydown: handleKeydown
}
};
data.class[`el-upload--${listType}`] = true;
return (
<div {...data} tabindex="0" >
{
drag
? <upload-dragger disabled={disabled} on-file={uploadFiles}>{this.$slots.default}</upload-dragger>
: this.$slots.default
}
<input class="el-upload__input" type="file" ref="input" name={name} on-change={handleChange} multiple={multiple} accept={accept}></input>
</div>
);
}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) {
if (this.limit && this.fileList.length + files.length > this.limit) {
this.onExceed && this.onExceed(files, this.fileList);
return;
}

let postFiles = Array.prototype.slice.call(files);
if (!this.multiple) { postFiles = postFiles.slice(0, 1); }

if (postFiles.length === 0) { return; }

postFiles.forEach(rawFile => {
this.onStart(rawFile);
if (this.autoUpload) this.upload(rawFile);
});
},
upload(rawFile) {
this.$refs.input.value = null;

if (!this.beforeUpload) {
return this.post(rawFile);
}

const before = this.beforeUpload(rawFile);
if (before && before.then) {
before.then(processedFile => {
const fileType = Object.prototype.toString.call(processedFile);

if (fileType === '[object File]' || fileType === '[object Blob]') {
if (fileType === '[object Blob]') {
processedFile = new File([processedFile], rawFile.name, {
type: rawFile.type
});
}
for (const p in rawFile) {
if (rawFile.hasOwnProperty(p)) {
processedFile[p] = rawFile[p];
}
}
this.post(processedFile);
} else {
this.post(rawFile);
}
}, () => {
this.onRemove(null, rawFile);
});
} else if (before !== false) {
this.post(rawFile);
} else {
this.onRemove(null, rawFile);
}
},

2.3实现自己的上传组件

基于上述的实现原理,实现一个自己的上传组件

element-ui源码研究-upload组件

核心代码:

<template>
<div class="wrapper">
<button class="upload-btn" @click="handleClick">
<span>上传</span>
<input
type="file"
ref="input"
:multiple="multiple"
@change="handleChange"
/>
</button>
<div class="file-list">
<div v-for="(item, index) in fileList" :key="index" class="file-item">
<span class="el-icon-document"></span>
<span>
{{ item.name }}
</span>
<span class="el-icon-close close-btn" @click="handleDelete"></span>
</div>
</div>
</div>
</template>

<script>
export default {
components: {},
props: {},
data() {
return {
multiple: true,
fileList: [],
}
},
computed: {},
watch: {},
created() {},
mounted() {},
activated() {},
deactivated() {},
updated() {},
destroyed() {},
methods: {
handleClick() {
this.$refs.input.value = null
this.$refs.input.click()
},
handleChange(ev) {
const files = ev.target.files

if (!files) return
this.uploadFiles(files)
},
uploadFiles(files) {
let postFiles = Array.prototype.slice.call(files)
if (!this.multiple) {
postFiles = postFiles.slice(0, 1)
}
if (postFiles.length === 0) {
return
}
this.fileList = postFiles
postFiles.forEach((rawFile) => {
console.log('rawFile', rawFile)
setTimeout(() => {
this.$message.success('上传成功')
}, 10)
})
},
handleDelete(index) {
this.$confirm('确定删除当前项吗', '提示', {
confirmButtonText: '确 定',
cancelButtonText: '取 消',
})
.then(() => {
this.fileList.splice(index, 1)
})
.catch(() => {})
},
},
filters: {},
}
</script>
<style scoped lang="scss">
.wrapper {
height: 100%;
position: relative;
border: 1px solid red;

.upload-btn {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
border: 1px solid #dcdfe6;
color: #606266;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
transition: 0.1s;
font-weight: 500;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
color: #fff;
background-color: #409eff;
border-color: #409eff;
input {
display: none;
}
}
.file-list {
.file-item {
padding: 5px 10px;
border-bottom: 1px solid #ccc;
display: flex;
align-items: center;
.close-btn {
margin-left: auto;
cursor: pointer;
}
}
}
}
</style>

这样一个自己的简易的上传组件就完成了,当然还可以扩展,在此不做实现,主要是明白element-ui 是如何实现上传组件的

3.总结

  1. upload组件实现
  2. 原生file事件如何隔山打牛触发及样式如何美化