Dieses Mal bringe ich Ihnen eine leichte Datei-Upload-Komponente, die in Vue gekapselt ist. Was sind die Vorsichtsmaßnahmen für die Kapselung einer leichten Datei-Upload-Komponente in Vue? Das Folgende ist ein praktischer Fall.
1. Einige zuvor aufgetretene Probleme
Es gibt viele Anforderungen für das Hochladen von Dateien im Projekt, die mithilfe des vorhandenen UI-Frameworks implementiert werden Während des Prozesses wird es aus unbekannten Gründen immer einige unerklärliche Fehler geben. Wenn Sie beispielsweise eine bestimmte Upload-Komponente verwenden, ist diese deutlich gekennzeichnet (:multiple="false"), tatsächlich ist es jedoch weiterhin möglich, mehrere Dateien auszuwählen, und beim Hochladen werden beispielsweise weiterhin mehrere Dateien gesendet (:file-list="fileList" wird hinzugefügt) ")-Attribut, wenn ich hoffe, die Upload-Liste manuell zu steuern, wird das Upload-Ereignis this.refs.[upload (component ref)].submit() nicht funktionieren und kann auch nicht sein übermittelt. Kurz gesagt, ich bin zu faul, um zu sehen, wie ich Funktionen verwende, und die Schnittstelle selbst muss noch neu geschrieben werden. Wenn ich darauf bestehe, wird dem Projekt viel unnötiger Logik- und Stilcode hinzugefügt ...
Vorher verwendetZu den von Vue für Projekte verwendeten View-Frameworks gehören element-ui, zp-ui als Ergänzung im Team und iview. Das Framework ist einfach zu verwenden, aber Sie können es oft nicht für Ihre eigenen Projekte verwenden. Insbesondere die von unserem Design-Girl erstellte Oberfläche unterscheidet sich stark vom vorhandenen Framework und kann leicht zu unbekannten Fehlern führen. Deshalb nehme ich mir die Zeit, es selbst zu tun. Kapselt diese Upload-Komponente.
2. Code und Einführung
Übergeordnete Komponente
<template> <p class="content"> <label for="my-upload"> <span>上传</span> </label> <my-upload ref="myUpload" :file-list="fileList" action="/uploadPicture" :data="param" :on-change="onChange" :on-progress="uploadProgress" :on-success="uploadSuccess" :on-failed="uploadFailed" multiple :limit="5" :on-finished="onFinished"> </my-upload> <button @click="upload" class="btn btn-xs btn-primary">Upload</button> </p> </template> <script> import myUpload from './components/my-upload' export default { name: 'test', data(){ return { fileList: [],//上传文件列表,无论单选还是支持多选,文件都以列表格式保存 param: {param1: '', param2: '' },//携带参数列表 } }, methods: { onChange(fileList){//监听文件变化,增减文件时都会被子组件调用 this.fileList = [...fileList]; }, uploadSuccess(index, response){//某个文件上传成功都会执行该方法,index代表列表中第index个文件 console.log(index, response); }, upload(){//触发子组件的上传方法 this.$refs.myUpload.submit(); }, removeFile(index){//移除某文件 this.$refs.myUpload.remove(index); }, uploadProgress(index, progress){//上传进度,上传时会不断被触发,需要进度指示时会很有用 const{ percent } = progress; console.log(index, percent); }, uploadFailed(index, err){//某文件上传失败会执行,index代表列表中第index个文件 console.log(index, err); }, onFinished(result){//所有文件上传完毕后(无论成败)执行,result: { success: 成功数目, failed: 失败数目 } console.log(result); } }, components: { myUpload } } </script>
Die übergeordnete Komponente verwaltet die geschäftsbezogene Logik, die ich speziell hinzugefügt habe Der Indexparameter ermöglicht es der Schnittstelle, den Wert beim Anzeigen des Upload-Ergebnisses direkt zu manipulieren. Er ist nicht für alle Methoden erforderlich und kann je nach Bedarf verwendet werden.
Unterkomponente
<template> <p> <input style="display:none" @change="addFile" :multiple="multiple" type="file" :name="name" id="my-upload"/> </p> </template>
Laden Sie die Datei hoch, der HTML-Teil besteht nur aus einem Tag-Paar. Die Komplexität gefällt mir nicht.
<script> export default { name: 'my-upload', props: { name: String, action: { type: String, required: true }, fileList: { type: Array, default: [] }, data: Object, multiple: Boolean, limit: Number, onChange: Function, onBefore: Function, onProgress: Function, onSuccess: Function, onFailed: Function, onFinished: Function }, methods: {}//下文主要是methods的介绍,此处先省略 } </script>
Hier werden die Attribute definiert, die vorhanden sein müssen Der Wert wird von der übergeordneten Komponente an die untergeordnete Komponente übergeben. Bitte beachten Sie, dass die Methode hier auch als Attribut übergeben wird, was akzeptabel ist.
Die von mir geschriebenen Komponenten sind nicht so vollständig und umfassend wie die von gängigen Frameworks veröffentlichten. Außerdem kann die eingangs erwähnte gebundene Dateiliste nicht hochgeladen werden (wahrscheinlicher aufgrund meiner Haltung). ist falsch), ich möchte auch mein Bestes geben, um dieses Problem zu lösen, daher hoffe ich, dass ich neben der Aktion auch die Dateiliste als Attribut verwenden kann, das übergeben werden muss die übergeordnete Komponente. (Der Attributname der übergeordneten Komponente ist mit „-“ verbunden, entsprechend der Camel-Case-Benennung in der untergeordneten Komponenten-Requisite)
3. Haupt-Upload-Funktion
methods: { addFile, remove, submit, checkIfCanUpload }
Es gibt 4 Methoden, darunter das Hinzufügen von Dateien, das Entfernen von Dateien, das Senden und das Testen (Testen vor dem Hochladen):
1. Dateien hinzufügen
addFile({target: {files}}){//input标签触发onchange事件时,将文件加入待上传列表 for(let i = 0, l = files.length; i < l; i++){ files[i].url = URL.createObjectURL(files[i]);//创建blob地址,不然图片怎么展示? files[i].status = 'ready';//开始想给文件一个字段表示上传进行的步骤的,后面好像也没去用...... } let fileList = [...this.fileList]; if(this.multiple){//多选时,文件全部压如列表末尾 fileList = [...fileList, ...files]; let l = fileList.length; let limit = this.limit; if(limit && typeof limit === "number" && Math.ceil(limit) > 0 && l > limit){//有数目限制时,取后面limit个文件 limit = Math.ceil(limit); // limit = limit > 10 ? 10 : limit; fileList = fileList.slice(l - limit); } }else{//单选时,只取最后一个文件。注意这里没写成fileList = files;是因为files本身就有多个元素(比如选择文件时一下子框了一堆)时,也只要一个 fileList = [files[0]]; } this.onChange(fileList);//调用父组件方法,将列表缓存到上一级data中的fileList属性 },
2. Verschieben Das Löschen von Dateien
ist manchmal einfach, wenn die übergeordnete Komponente eine Datei verzweigt.
remove(index){ let fileList = [...this.fileList]; if(fileList.length){ fileList.splice(index, 1); this.onChange(fileList); } },
3. Upload senden
Hier werden zwei Methoden verwendet: Fetch und native Methode. Da Fetch das Abrufen des Upload-Fortschritts nicht unterstützt, benötigen Sie keinen Fortschrittsbalken oder simulieren ihn Progress selbst oder XMLHttpRequest-Objekt Wenn es nicht vorhanden ist, ist es einfacher, fetch zum Anfordern der Upload-Logik zu verwenden
submit(){ if(this.checkIfCanUpload()){ if(this.onProgress && typeof XMLHttpRequest !== 'undefined') this.xhrSubmit(); else this.fetchSubmit(); } },
4 Basierend auf den beiden Sätzen der Upload-Logik sind hier zwei Methoden xhrSubmit und fetchSubmit gekapselt
fetchSubmit
fetchSubmit(){ let keys = Object.keys(this.data), values = Object.values(this.data), action = this.action; const promises = this.fileList.map(each => { each.status = "uploading"; let data = new FormData(); data.append(this.name || 'file', each); keys.forEach((one, index) => data.append(one, values[index])); return fetch(action, { method: 'POST', headers: { "Content-Type" : "application/x-www-form-urlencoded" }, body: data }).then(res => res.text()).then(res => JSON.parse(res));//这里res.text()是根据返回值类型使用的,应该视情况而定 }); Promise.all(promises).then(resArray => {//多线程同时开始,如果并发数有限制,可以使用同步的方式一个一个传,这里不再赘述。 let success = 0, failed = 0; resArray.forEach((res, index) => { if(res.code == 1){ success++; //统计上传成功的个数,由索引可以知道哪些成功了 this.onSuccess(index, res); }else if(res.code == 520){ //约定失败的返回值是520 failed++; //统计上传失败的个数,由索引可以知道哪些失败了 this.onFailed(index, res); } }); return { success, failed }; //上传结束,将结果传递到下文 }).then(this.onFinished); //把上传总结果返回 },
xhrSubmit
xhrSubmit(){ const _this = this; let options = this.fileList.map((rawFile, index) => ({ file: rawFile, data: _this.data, filename: _this.name || "file", action: _this.action, onProgress(e){ _this.onProgress(index, e);//闭包,将index存住 }, onSuccess(res){ _this.onSuccess(index, res); }, onError(err){ _this.onFailed(index, err); } })); let l = this.fileList.length; let send = async options => { for(let i = 0; i < l; i++){ await _this.sendRequest(options[i]);//这里用了个异步方法,按次序执行this.sendRequest方法,参数为文件列表包装的每个对象,this.sendRequest下面紧接着介绍 } }; send(options); },
Dies basiert auf dem Upload-Quellcode von element-ui
sendRequest(option){ const _this = this; upload(option); function getError(action, option, xhr) { var msg = void 0; if (xhr.response) { msg = xhr.status + ' ' + (xhr.response.error || xhr.response); } else if (xhr.responseText) { msg = xhr.status + ' ' + xhr.responseText; } else { msg = 'fail to post ' + action + ' ' + xhr.status; } var err = new Error(msg); err.status = xhr.status; err.method = 'post'; err.url = action; return err; } function getBody(xhr) { var text = xhr.responseText || xhr.response; if (!text) { return text; } try { return JSON.parse(text); } catch (e) { return text; } } function upload(option) { if (typeof XMLHttpRequest === 'undefined') { return; } var xhr = new XMLHttpRequest(); var 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); }; } var formData = new FormData(); if (option.data) { Object.keys(option.data).map(function (key) { formData.append(key, option.data[key]); }); } formData.append(option.filename, option.file); 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); if (option.withCredentials && 'withCredentials' in xhr) { xhr.withCredentials = true; } var headers = option.headers || {}; for (var item in headers) { if (headers.hasOwnProperty(item) && headers[item] !== null) { xhr.setRequestHeader(item, headers[item]); } } xhr.send(formData); return xhr; } }
Fügen Sie abschließend die Verifizierung vor der Anfrage hinzu
checkIfCanUpload(){ return this.fileList.length ? (this.onBefore && this.onBefore() || !this.onBefore) : false; },
Wenn die übergeordnete Komponente die onBefore-Methode definiert. Wenn false zurückgegeben wird oder die Dateiliste leer ist, wird die Anfrage nicht gesendet.
Ich glaube, dass Sie die Methode beherrschen, nachdem Sie den Fall in diesem Artikel gelesen haben. Weitere spannende Informationen finden Sie in anderen verwandten Artikeln auf der chinesischen PHP-Website!
Empfohlene Lektüre:
p5.js verwendet die Funktion draw(), um eine goldene Spirale zu erstellen
Verwendung es in tatsächlichen Projekten jquery filter()
Das obige ist der detaillierte Inhalt vonvue kapselt eine leichtgewichtige Datei-Upload-Komponente. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!