分析el-upload
“name”: “element-ui”,
“version”: “2.10.1”,
组件文档: 文档
源码在packages/upload/
下: 链接
1 2 3 4 5 6 7 8
| |-- upload |-- index.js |-- src |-- ajax.js |-- index.vue |-- upload-dragger.vue |-- upload-list.vue |-- upload.vue
|
样式在packages/theme-chalk/src/index.scss
index.vue
整体集成了各组件:
Upload组件
UploadList组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
import UploadList from './upload-list'; import Upload from './upload';
render(h) {
const trigger = this.$slots.trigger || this.$slots.default; const uploadComponent = <upload {...uploadData}>{trigger}</upload>;
return ( <div> { this.listType === 'picture-card' ? uploadList : ''} { this.$slots.trigger ? [uploadComponent, this.$slots.default] : uploadComponent } {this.$slots.tip} { this.listType !== 'picture-card' ? uploadList : ''} </div> ); }
|
Upload组件
upload.vue
藏了一个获取文件所需的<input>
,
若需响应拖放, 则在内容外层包一个注册了drag事件的upload-dragger
1 2 3 4 5 6 7 8
| <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>
|
$slots.default
是上层index.vue
传下来的”trigger”
1 2 3 4 5 6 7 8 9
| const data = { class: { 'el-upload': true }, on: { click: handleClick, keydown: handleKeydown } };
|
...data
里注册了点击事件和键盘事件(space键和enter键), 转而触发<input>
的click事件 this.$refs.input.click()
所以trigger slot里的并不是真正的trigger, 它爹才是, 只不过它爹捕获了冒泡上来的click事件
jsx相关用法见vue官方文档
upload-dragger.vue
注册了drag相关事件
1 2 3 4 5 6 7 8 9 10 11 12 13
| <template> <div class="el-upload-dragger" :class="{ 'is-dragover': dragover }" @drop.prevent="onDrop" @dragover.prevent="onDragover" @dragleave.prevent="dragover = false" > <slot></slot> </div> </template>
|
onDrop
内this.$emit('file', e.dataTransfer.files)
给上级的upload.vue处理
参考 DataTransfer.files
文件上传
挨个文件调用this.post()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
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]; } }; const req = this.httpRequest(options); this.reqs[uid] = req; if (req && req.then) { req.then(options.onSuccess, options.onError); } },
|
httpRequest
方法的默认实现在同文件夹的ajax.js
里, 也可由<el-upload>
的http-request
属性传入
(element-ui官方文档没写清楚http-request
的参数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
export default function upload(option) { const formData = new FormData(); formData.append(option.filename, option.file, option.file.name);
const xhr = new XMLHttpRequest(); if (xhr.upload) { xhr.upload.onprogress = function progress(e) { if (e.total > 0) { e.percent = e.loaded / e.total * 100; } option.onProgress(e); }; } xhr.onerror = function error(e) { }; xhr.onload = function onload() { }; xhr.open('post', option.action , true); xhr.withCredentials = true; xhr.setRequestHeader( ); xhr.send(formData); return xhr; }
|
XMLHttpRequest.upload用法见文档
UploadList组件
封装了三种不同显示方式
list-type: “text”
list-type: “picture”
list-type: “picture-card”