首頁 web前端 js教程 Vue封裝一個簡單輕量的上傳檔案元件的範例

Vue封裝一個簡單輕量的上傳檔案元件的範例

May 26, 2018 pm 04:46 PM
上傳 簡單 輕量

這篇文章主要介紹了Vue封裝一個簡單輕量的上傳檔案元件的範例,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧

一、之前遇到的一些問題

專案中多出有上傳檔案的需求,在使用現有的UI框架實現的過程中,不知道什麼原因,總是會有一些莫名其妙的bug。例如用某上傳元件,明明註明(:multiple="false"),可實際上還是能多選,上傳的時候依然發送了多個文件;又例如只要加上了(:file-list="fileList ")屬性,希望能手動控制上傳清單的時候,上傳事件this.refs.[upload(元件ref)].submit()就不起作用了,傳不了。總之,我懶得再看它怎麼實現了,我用的是功能,介面本身還是要重寫的,如果堅持用也會使項目多很多不必要的邏輯、樣式代碼……

之前用Vue做專案用的視野架構有element-ui,團隊內部作為補充的zp-ui,以及iview。框架是好用,但是針對自己的專案往往不能全部拿來用,尤其是我們的設計妹子出的介面與現有框架差異很大,改源碼效率低又容易導致未知的bug,於是自己就抽時間封裝了這個上傳元件。

二、程式碼與介紹

父元件

##

<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 &#39;./components/my-upload&#39;
export default {
 name: &#39;test&#39;,
 data(){
  return {
  fileList: [],//上传文件列表,无论单选还是支持多选,文件都以列表格式保存
  param: {param1: &#39;&#39;, param2: &#39;&#39; },//携带参数列表
  }
 },
 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>
登入後複製

#父元件處理與業務有關的邏輯,我特意加入索引參數,以便於介面展示上傳結果的時候能夠直接操作第幾個值,並不是所有方法都必須的,視需求使用。

子元件

<template>
<p>
 <input style="display:none" @change="addFile" :multiple="multiple" type="file" :name="name" id="my-upload"/>
</p>
</template>
登入後複製

上傳文件,html部分就這麼一對兒標籤,不喜歡複雜囉嗦

#

<script>
export default {
 name: &#39;my-upload&#39;,
 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>
登入後複製

這裡定義了父元件向子元件需要傳遞的屬性值,注意,這裡把方法也當做了屬性傳遞,都是可以的。

自己寫的元件,沒有像流行框架發布的那樣完備和全面,另外針對開頭提到的綁定file-list就不能上傳了的問題(更可能是我的姿勢不對),本人也想盡力解決掉自身遇到的這個問題,所以希望能對文件清單有絕對的控制權,除了action,把file-list也當作父元件必須要傳遞的屬性。 (屬性名稱父元件使用「-」連接,對應子元件prop中的駝峰命名)

#三、主要的上傳功能

##
methods: {
  addFile, remove, submit, checkIfCanUpload
}
登入後複製

methods內共4個方法,新增檔案、移除檔案、提交、偵測(上傳之前的檢驗),以下一一講述:

##1.新增檔案

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.移除檔案

這個簡單,有時候在父元件叉掉某檔的時候,傳一個index即可。

remove(index){
 let fileList = [...this.fileList];
 if(fileList.length){
 fileList.splice(index, 1);
 this.onChange(fileList);
 }
},
登入後複製

3.提交上傳

這裡使用了兩種方式,fetch和原生方式,由於fetch不支援取得上傳的進度,如果不需要進度條或自己模擬進度或XMLHttpRequest物件不存在的時候,使用fetch請求上傳邏輯會更簡單一些

submit(){
 if(this.checkIfCanUpload()){
 if(this.onProgress && typeof XMLHttpRequest !== &#39;undefined&#39;)
  this.xhrSubmit();
 else
  this.fetchSubmit();
 }
},
登入後複製

4.基於上傳的兩套邏輯,這裡封裝了兩個方法xhrSubmit和fetchSubmit

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 || &#39;file&#39;, each);
 keys.forEach((one, index) => data.append(one, values[index]));
 return fetch(action, {
  method: &#39;POST&#39;,
  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);
},
登入後複製

這裡借鏡了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(&#39;post&#39;, action, true);

  if (option.withCredentials && &#39;withCredentials&#39; 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;
 }
}
登入後複製

最後把請求前的校驗加上

checkIfCanUpload(){
 return this.fileList.length ? (this.onBefore && this.onBefore() || !this.onBefore) : false;
},
登入後複製

如果父元件定義了onBefore方法且回傳了false,或是檔案清單為空,請求就不會傳送。

程式碼部分完了,使用時只要有了on-progress屬性並且XMLHttpRequest物件可訪問,就會使用原生方式發送請求,否則就用fetch發送請求(不展示進度)。


上面是我整理給大家的,希望今後對大家有幫助。

相關文章:

Ajax表單異步上傳檔案實例代碼

下拉式選單的級聯操作

Ajax實作省市區三級級聯


以上是Vue封裝一個簡單輕量的上傳檔案元件的範例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

最簡單的硬碟序號查詢方式 最簡單的硬碟序號查詢方式 Feb 26, 2024 pm 02:24 PM

硬碟序號是硬碟的一個重要標識,通常用於唯一標識硬碟以及進行硬體識別。在某些情況下,我們可能需要查詢硬碟序號,例如在安裝作業系統、尋找正確裝置驅動程式或進行硬碟維修等情況下。本文將介紹一些簡單的方法,幫助大家查詢硬碟序號。方法一:使用Windows命令提示字元開啟命令提示字元。在Windows系統中,按下Win+R鍵,輸入"cmd"並按下回車鍵即可開啟命

如何在FastAPI中實現檔案上傳和處理 如何在FastAPI中實現檔案上傳和處理 Jul 28, 2023 pm 03:01 PM

如何在FastAPI中實現檔案上傳和處理FastAPI是一個現代化的高效能Web框架,簡單易用且功能強大,它提供了原生支援檔案上傳和處理的功能。在本文中,我們將學習如何在FastAPI框架中實作檔案上傳和處理的功能,並提供程式碼範例來說明具體的實作步驟。首先,我們需要導入需要的函式庫和模組:fromfastapiimportFastAPI,UploadF

Win10電腦上傳速度慢怎麼解決 Win10電腦上傳速度慢怎麼解決 Jul 01, 2023 am 11:25 AM

  Win10電腦上傳速度慢怎麼解決?我們在使用電腦的時候可能會覺得自己電腦上傳檔案的速度非常的慢,那麼這是什麼情況呢?其實這是因為電腦預設的上傳速度為20%,所以才導致上傳速度非常慢,很多小夥伴不知道怎麼詳細操作,小編下面整理了win11格式化c碟操作步驟,如果你有興趣的話,跟著小編一起往下看看吧!  Win10上傳速度慢的解決方法  1、按下win+R調出運行,輸入gpedit.msc,回車。  2、選擇管理模板,點選網路--Qos封包計畫程序,雙擊限制可保留頻寬。  3、選擇已啟用,將帶

如何在QQ音樂上傳歌詞 如何在QQ音樂上傳歌詞 Feb 23, 2024 pm 11:45 PM

隨著數位時代的到來,音樂平台成為人們獲取音樂的主要途徑之一。然而,有時候我們在聽歌的時候,發現沒有歌詞是一件十分困擾的事。許多人都希望在聽歌的時候能夠展現歌詞,以便更好地理解歌曲的內容和情感。而QQ音樂作為國內最大的音樂平台之一,也為用戶提供了上傳歌詞的功能,使得用戶可以更好地享受音樂的同時,感受到歌曲的內涵。以下將介紹一下在QQ音樂上如何上傳歌詞。首先

酷狗上傳自己的音樂的簡單步驟 酷狗上傳自己的音樂的簡單步驟 Mar 25, 2024 pm 10:56 PM

1.打開酷狗音樂,點選個人頭像。 2、點選右上角設定的圖示。 3.點選【上傳音樂作品】。 4.點選【上傳作品】。 5.選擇歌曲,然後點選【下一步】。 6.最後點選【上傳】即可。

如何使用Java來寫一個簡單的學生成績報表產生器? 如何使用Java來寫一個簡單的學生成績報表產生器? Nov 03, 2023 pm 02:57 PM

如何使用Java來寫一個簡單的學生成績報表產生器?學生成績報表產生器是可以幫助老師或教育者快速產生學生成績報告的工具。本文將介紹如何使用Java來撰寫簡單的學生成績報表產生器。首先,我們要定義學生對象和學生成績對象。學生對象包含學生的姓名、學號等基本訊息,而學生成績對象則包含學生的科目成績和平均成績等資訊。以下是一個簡單的學生物件的定義:public

如何透過PHP編寫一個簡單的線上預約系統 如何透過PHP編寫一個簡單的線上預約系統 Sep 26, 2023 pm 09:55 PM

如何透過PHP編寫一個簡單的線上預約系統隨著網路的普及和使用者對便利性的追求,線上預約系統越來越受到歡迎。無論是餐廳、醫院、美容院或其他服務業,都可以透過簡單的線上預約系統來提高效率並為使用者提供更好的服務體驗。本文將介紹如何使用PHP編寫一個簡單的線上預約系統,並提供具體的程式碼範例。建立資料庫和表格首先,我們需要建立一個資料庫來儲存預約資訊。在MyS

如何提升電腦上傳速度 如何提升電腦上傳速度 Jan 15, 2024 pm 06:51 PM

上傳速度變得非常慢?相信這是很多朋友用電腦上傳東西時候都會遇到的一個問題,在使用電腦傳送檔案的時候如果遇到網路不穩定,上傳的速度就會很慢,那麼該怎麼提高網路上傳速度呢?下面,小編將電腦上傳速度慢的處理方式告訴大家。說到網路速度,我們都知道打開網頁的速度,下載速度,其實還有一個上傳速度也非常關鍵,特別是一些用戶經常需要上傳文件到網盤的,那麼上傳速度快無疑會給你省下不少時間,那上傳速度慢怎麼辦?下面,小編為大夥帶來了電腦上傳速度慢的處理圖文。電腦上傳速度慢怎麼解決點選「開始--執行」或「視窗鍵

See all articles