Vue封裝上傳檔案元件步驟詳解(附程式碼)
這次帶給大家Vue封裝上傳檔案元件步驟詳解(附程式碼),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 './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>
父元件處理與業務相關的邏輯,我刻意加入索引參數,方便介面展示上傳結果的時候能夠直接操作第幾個值,並不是所有方法都必須的,視需求使用。
子元件
<template> <p> <input style="display:none" @change="addFile" :multiple="multiple" type="file" :name="name" id="my-upload"/> </p> </template>
上傳文件,html部分就這麼一對兒標籤,不喜歡複雜囉嗦
<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>
這裡定義了父元件向子元件需要傳遞的屬性值,注意,這裡把方法也當做了屬性傳遞,都是可以的。
自己寫的元件,沒有像流行框架發布的那樣完備和全面,另外針對開頭提到的綁定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 = '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.移除檔案
這個簡單,有時候在父元件叉掉某檔案的時候,傳一個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 !== 'undefined') 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 || '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); },
這裡借鑒了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; } }
最後把請求前的校驗加上
checkIfCanUpload(){ return this.fileList.length ? (this.onBefore && this.onBefore() || !this.onBefore) : false; },
如果父元件定義了onBefore方法且回傳了false,或是檔案清單為空,要求就不會發送。
程式碼部分完了,使用時只要有了on-progress屬性並且XMLHttpRequest物件可訪問,就會使用原生方式發送請求,否則就用fetch發送請求(不展示進度)。
相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!
推薦閱讀:
#以上是Vue封裝上傳檔案元件步驟詳解(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

iPhone上的預設地圖是Apple專有的地理位置供應商「地圖」。儘管地圖越來越好,但它在美國以外的地區運作不佳。與谷歌地圖相比,它沒有什麼可提供的。在本文中,我們討論了使用Google地圖成為iPhone上的預設地圖的可行性步驟。如何在iPhone中使Google地圖成為預設地圖將Google地圖設定為手機上的預設地圖應用程式比您想像的要容易。請依照以下步驟操作–先決條件步驟–您必須在手機上安裝Gmail。步驟1–開啟AppStore。步驟2–搜尋“Gmail”。步驟3–點選Gmail應用程式旁

不斷推出新版本以提供更好的使用體驗,微信作為中國的社交媒體平台之一。升級微信至最新版本是非常重要的,家人和同事的聯繫、為了保持與朋友、及時了解最新動態。 1.了解最新版本的特性與改進了解最新版本的特性與改進非常重要,在升級微信之前。效能改進和錯誤修復,透過查看微信官方網站或應用程式商店中的更新說明、你可以了解新版本所帶來的各種新功能。 2.檢查目前微信版本我們需要檢查目前手機上已安裝的微信版本、在升級微信之前。點擊,打開微信應用“我”然後選擇,菜單“關於”在這裡你可以看到當前微信的版本號,。 3.打開應

使用AppleID登入iTunesStore時,可能會在螢幕上拋出此錯誤提示「此AppleID尚未在iTunesStore中使用」。沒有什麼可擔心的錯誤提示,您可以按照這些解決方案集進行修復。修正1–更改送貨地址此提示出現在iTunesStore中的主要原因是您的AppleID個人資料中沒有正確的地址。步驟1–首先,開啟iPhone上的iPhone設定。步驟2–AppleID應位於所有其他設定的頂部。所以,打開它。步驟3–在那裡,打開“付款和運輸”選項。步驟4–使用面容ID驗證您的存取權限。步驟

iPhone上的Shazam應用程式有問題? Shazam可協助您透過聆聽歌曲找到歌曲。但是,如果Shazam無法正常工作或無法識別歌曲,則必須手動對其進行故障排除。修復Shazam應用程式不會花費很長時間。因此,無需再浪費時間,請按照以下步驟解決Shazam應用程式的問題。修正1–禁用粗體文字功能iPhone上的粗體文字可能是Shazam無法正常運作的原因。步驟1–您只能從iPhone設定執行此操作。所以,打開它。步驟2–接下來,開啟其中的「顯示和亮度」設定。步驟3–如果您發現啟用了“粗體文本

在電腦中刪除或解壓縮資料夾,時有時會彈出提示對話框“錯誤0x80004005:未指定錯誤”,如果遇到這中情況應該怎麼解決呢?提示錯誤碼0x80004005的原因其實很多,但大部分因為病毒導致,我們可以重新註冊dll來解決問題,下面,小編給大夥講解0x80004005錯誤代碼處理經驗。有使用者在使用電腦時出現錯誤代碼0X80004005的提示,0x80004005錯誤主要是由於電腦沒有正確註冊某些動態連結庫文件,或電腦與Internet之間存在不允許的HTTPS連接防火牆所引起。那麼如何

螢幕截圖功能在您的iPhone上不起作用嗎?截圖非常簡單,因為您只需同時按住「提高音量」按鈕和「電源」按鈕即可抓取手機螢幕。但是,還有其他方法可以在設備上捕獲幀。修復1–使用輔助觸控使用輔助觸控功能截取螢幕截圖。步驟1–轉到您的手機設定。步驟2–接下來,點選以開啟「輔助功能」設定。步驟3–開啟「觸摸」設定。步驟4–接下來,開啟「輔助觸控」設定。步驟5–打開手機上的「輔助觸控」。步驟6–打開“自訂頂級選單”以存取它。步驟7–現在,您只需將這些功能中的任何一個連結到螢幕擷取即可。因此,點擊那裡的首

您的手機中缺少時鐘應用程式嗎?日期和時間仍將顯示在iPhone的狀態列上。但是,如果沒有時鐘應用程序,您將無法使用世界時鐘、碼錶、鬧鐘等多項功能。因此,修復時鐘應用程式的缺失應該是您的待辦事項清單的首位。這些解決方案可以幫助您解決此問題。修復1–放置時鐘應用程式如果您錯誤地從主畫面中刪除了時鐘應用程序,您可以將時鐘應用程式放回原位。步驟1–解鎖iPhone並開始向左側滑動,直到到達「應用程式庫」頁面。步驟2–接下來,在搜尋框中搜尋「時鐘」。步驟3–當您在搜尋結果中看到下方的「時鐘」時,請按住它並

在iPhone上面臨滯後,緩慢的行動數據連線?通常,手機上蜂窩互聯網的強度取決於幾個因素,例如區域、蜂窩網絡類型、漫遊類型等。您可以採取一些措施來獲得更快、更可靠的蜂窩網路連線。修復1–強制重啟iPhone有時,強制重啟設備只會重置許多內容,包括蜂窩網路連線。步驟1–只需按一次音量調高鍵並放開即可。接下來,按降低音量鍵並再次釋放它。步驟2–過程的下一部分是按住右側的按鈕。讓iPhone完成重啟。啟用蜂窩數據並檢查網路速度。再次檢查修復2–更改資料模式雖然5G提供了更好的網路速度,但在訊號較弱
