Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung der Schritte zum Kapseln der Lightweight-Upload-Dateikomponente in Vue

Detaillierte Erläuterung der Schritte zum Kapseln der Lightweight-Upload-Dateikomponente in Vue

php中世界最好的语言
php中世界最好的语言Original
2018-05-09 10:20:112139Durchsuche

Dieses Mal erkläre ich Ihnen ausführlich die Schritte zum Kapseln der Lightweight-Upload-Dateikomponente in Vue. Was sind die Vorsichtsmaßnahmen für die Kapselung der Lightweight-Upload-Dateikomponente in Vue? , lass uns einen Blick darauf werfen.

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 verwendet

Zu 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 insgesamt 4 Methoden, darunter das Hinzufügen von Dateien, das Entfernen von Dateien, das Senden und das Testen (Testen vor dem Hochladen):

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 = &#39;ready&#39;;//开始想给文件一个字段表示上传进行的步骤的,后面好像也没去用......
 }
 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, Abruf und native Methode, da der Abruf den Upload-Fortschritt nicht unterstützt, wenn Sie keinen Fortschrittsbalken oder Wenn Sie den Fortschritt selbst simulieren oder das XMLHttpRequest-Objekt 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, zwei Methoden xhrSubmit und fetchSubmit sind hier 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 + &#39; &#39; + (xhr.response.error || xhr.response);
  } else if (xhr.responseText) {
   msg = xhr.status + &#39; &#39; + xhr.responseText;
  } else {
   msg = &#39;fail to post &#39; + action + &#39; &#39; + xhr.status;
  }
  var err = new Error(msg);
  err.status = xhr.status;
  err.method = &#39;post&#39;;
  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 === &#39;undefined&#39;) {
   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 das hinzu Überprüfung vor der Anfrage

checkIfCanUpload(){
 return this.fileList.length ? (this.onBefore && this.onBefore() || !this.onBefore) : false;
},
wenn die übergeordnete Komponente die onBefore-Methode definiert und false zurückgibt oder die Dateiliste leer ist, wird die Anfrage nicht gesendet.

Der Codeteil ist fertig, solange das on-progress-Attribut vorhanden ist und auf das XMLHttpRequest-Objekt zugegriffen werden kann. Andernfalls wird die Anforderung gesendet mit fetch (der Fortschritt wird nicht angezeigt).

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:

Anwendungsfallbeschreibung der filter()-Methode in jquery

Detaillierte Erläuterung der Schritte dazu Verwenden Sie WebUploader in der Fuzzy-Box

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Schritte zum Kapseln der Lightweight-Upload-Dateikomponente in Vue. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn