欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ElementUI?Upload源碼組件上傳流程解析

 更新時(shí)間:2022年11月24日 09:01:37   作者:CodeListener  
這篇文章主要為大家介紹了ElementUI?Upload源碼組件上傳流程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

很多時(shí)候我們都一直使用ElementUIUpload上傳組件進(jìn)行二次封裝, 但是否知道內(nèi)部是什么樣的一個(gè)上傳流程,事件在哪個(gè)時(shí)機(jī)觸發(fā),從獲取文件到上傳結(jié)束究竟經(jīng)歷什么樣的一個(gè)過程?希望通過分析該組件的核心邏輯 (不包括UI邏輯) 讓你在后續(xù)的開發(fā)中能夠快速定位問題所在

源文件

訪問packages/upload目錄可以看到如下內(nèi)容,其主要核心代碼在upload.vueindex.vue,單純一個(gè)文件一個(gè)文件看代碼理解雖然能看得懂,但是比較難把整個(gè)邏輯串通,所以我們從文件的獲取到上傳結(jié)束開始逐一分析

│  index.js
└─ src
    ajax.js              [默認(rèn)上傳請(qǐng)求工具]
    index.vue            [管理FileList數(shù)據(jù),對(duì)外暴露操作文件列表的方法]
    upload-dragger.vue   [拖拽:對(duì)文件獲取的邏輯]
    upload-list.vue      [文件列表:純UI組件根據(jù)不同listType展示不同的樣式]
    upload.vue           [對(duì)單個(gè)文件上傳處理的過程],會(huì)涉及index.vue文件邏輯操作]

流程圖

1??. 獲取文件

??? upload.vue創(chuàng)建input組件同時(shí)設(shè)置display:none進(jìn)行隱藏,只通過ref進(jìn)行引用觸發(fā)$refs.input.click(),通過監(jiān)聽(13: Enter鍵) 32: (空格鍵)和點(diǎn)擊上傳容器觸發(fā)

  • handleClick
  • handleKeydown
  • 拖拽(只是在拖拽結(jié)束獲取文件觸發(fā)uploadFiles,具體邏輯在upload-dragger.vue比較簡(jiǎn)單,所以不對(duì)其進(jìn)行分析)
methods: {
    handleClick() {
      if (!this.disabled) {
        // 處理選中文件之后,后續(xù)繼續(xù)選中重復(fù)文件無法觸發(fā)change問題
        this.$refs.input.value = null;
        this.$refs.input.click();
      }
    },
    handleKeydown(e) {
      if (e.target !== e.currentTarget) return;
      if (e.keyCode === 13 || e.keyCode === 32) {
        this.handleClick();
      }
    }
},
 render(h) {
    // ...
    const data = {
      on: {
        click: handleClick,
        keydown: handleKeydown
      }
    };
    return (
      <div {...data} tabindex="0" >
        // ...
        <input class="el-upload__input" type="file" ref="input" name={name} on-change={handleChange} multiple={multiple} accept={accept}></input>
      </div>
    );
  }

2??. 文件個(gè)數(shù)校驗(yàn)

在觸發(fā)input的handleChange后開始我們的校驗(yàn)階段(uploadFiles方法):

?? 校驗(yàn)文件最大個(gè)數(shù)

如果有設(shè)置limit個(gè)數(shù)限制時(shí),判斷當(dāng)前選中的文件和已有的文件總和是否超出最大個(gè)數(shù),是的話則觸發(fā)onExceed事件同時(shí)退出

// ??? upload.vue uploadFiles
if (this.limit && this.fileList.length + files.length > this.limit) {
    this.onExceed && this.onExceed(files, this.fileList);
    return;
}

?? 非多文件情況處理

如果multiple未設(shè)置或者為false時(shí),只獲取選中的第一個(gè)文件,如果沒有選中的文件則退出

// ??? upload.vue uploadFiles
let postFiles = Array.prototype.slice.call(files);
if (!this.multiple) { postFiles = postFiles.slice(0, 1); }
if (postFiles.length === 0) { return; }

3??. 構(gòu)造FileItem對(duì)象

onStart

handleStart

FileItem對(duì)象

key描述
status文件狀態(tài): ready, uploading, success, fail
name文件名稱
size文件大小
percentage上傳進(jìn)度
uid文件uid
raw原始文件對(duì)象

遍歷每一個(gè)選中的文件,根據(jù)我們需要的信息構(gòu)建我們所的文件對(duì)象同時(shí)放入fileList數(shù)組中,同時(shí)status狀態(tài)為ready準(zhǔn)備上傳階段,判斷如果listType=picture-card | picture根據(jù)文件設(shè)置url: 生成blobURL進(jìn)行回顯 (不需要等待上傳完成才能看到圖片內(nèi)容), 接著觸發(fā)onChange事件

// upload.vue (onStart) ---> index.vue (handleStart)
handleStart(rawFile) {
  rawFile.uid = Date.now() + this.tempIndex++;
  let file = {
    status: 'ready',
    name: rawFile.name,
    size: rawFile.size,
    percentage: 0,
    uid: rawFile.uid,
    raw: rawFile
  };
  if (this.listType === 'picture-card' || this.listType === 'picture') {
    try {
      file.url = URL.createObjectURL(rawFile);
    } catch (err) {
      console.error('[Element Error][Upload]', err);
      return;
    }
  }
  this.uploadFiles.push(file);
  this.onChange(file, this.uploadFiles);
}

4??. 上傳階段

緊接著判斷auto-upload是否自動(dòng)上傳

  • true : 自動(dòng)觸發(fā)upload方法
  • false: 通過外部手動(dòng)觸發(fā)$refs.upload.submit()手動(dòng)觸發(fā)時(shí)通過過濾出status=ready準(zhǔn)備上傳的文件遍歷觸發(fā)upload方法
// index.vue
submit() {
  this.uploadFiles
    .filter(file => file.status === 'ready')
    .forEach(file => {
      this.$refs['upload-inner'].upload(file.raw);
    });
}

?? beforeUpload前置操作

根據(jù)外部是否傳入beforeUpload,可以對(duì)預(yù)備上傳的文件進(jìn)行預(yù)處理或者校驗(yàn)是否可以上傳:

  • 不傳beforeUpload直接觸發(fā)post方法上傳
// ??? upload.vue upload
if (!this.beforeUpload) {
    return this.post(rawFile);
}

beforeUpload(同步異步):

  • 同步:傳入一個(gè)方法后返回false即終止上傳,其他非Promise類型結(jié)果的都通過上傳
  • 異步:當(dāng)被reject則中斷上傳過程,當(dāng)resolvef返回一個(gè)新的文件或者Blob類型數(shù)據(jù),根據(jù)原始的文件對(duì)象,重新構(gòu)建出新的文件對(duì)象進(jìn)行上傳(resolve返回任意值都會(huì)觸發(fā)上傳)
// ??? upload.vue upload
const before = this.beforeUpload(rawFile);
// Promise
if (before && before.then) {
    before.then(processedFile => {
      const fileType = Object.prototype.toString.call(processedFile);
      if (fileType === '[object File]' || fileType === '[object Blob]') {
        if (fileType === '[object Blob]') {
          processedFile = new File([processedFile], rawFile.name, {
            type: rawFile.type
          });
        }
        // 將原始值復(fù)制到新構(gòu)建的File對(duì)象中
        for (const p in rawFile) {
          if (rawFile.hasOwnProperty(p)) {
            processedFile[p] = rawFile[p];
          }
        }
        this.post(processedFile);
      } else {
        this.post(rawFile);
      }
    }, () => {
      // 停止上傳并且從文件列表中刪除
      this.onRemove(null, rawFile);
    });
}
// 非false正常上傳
else if (before !== false) {
    this.post(rawFile);
} else {
// 停止上傳并且從文件列表中刪除
    this.onRemove(null, rawFile);
}

? beforeUpload中斷情況

onRemove

handleRemove

beforeRemove可選

doRemove

當(dāng)beforeUpload返回false或者reject時(shí),會(huì)觸發(fā)onRemove方法(index.vue: handleRemove)

?? 這里觸發(fā)onRemove其實(shí)有個(gè)坑點(diǎn),當(dāng)你beforeUpload被中斷時(shí)會(huì)觸發(fā)onRemove對(duì)文件進(jìn)行刪除,如果你傳入了beforeRemove同時(shí)彈出確認(rèn)框確認(rèn)刪除操作,這會(huì)導(dǎo)致上傳中斷時(shí)顯示出來,這會(huì)讓用戶感覺到突兀,有個(gè)比較粗糙的方式就是在beforeRemove判斷status!=ready,即準(zhǔn)備上傳的文件不需要走beforeRemove確認(rèn)直接刪除

 // index.vue
 handleRemove(file, raw) {
     if (raw) {
       // 獲取當(dāng)前的FileItem對(duì)象
       file = this.getFile(raw);
     }
     let doRemove = () => {
       // 中斷正在上傳的文件
       this.abort(file);
       // 移除當(dāng)前FileItem
       let fileList = this.uploadFiles;
       fileList.splice(fileList.indexOf(file), 1);
       // 觸發(fā)回調(diào)
       this.onRemove(file, fileList);
     };
     // 外部沒有傳入beforeRemove直接操作刪除
     if (!this.beforeRemove) {
       doRemove();
     } else if (typeof this.beforeRemove === 'function') {
       const before = this.beforeRemove(file, this.uploadFiles);
       // 和 beforeUpload類似邏輯
       if (before && before.then) {
         before.then(() => {
           doRemove();
         }, noop);
       } else if (before !== false) {
         doRemove();
       }
     }
   }

?? 上傳

構(gòu)造HttpRequest的Options參數(shù)

發(fā)起請(qǐng)求并且緩存當(dāng)前請(qǐng)求實(shí)例以便后續(xù)可終止

  • 構(gòu)造參數(shù),即httpRequest需要的請(qǐng)求對(duì)象 options
key描述
headers請(qǐng)求headers
withCredentials發(fā)送 cookie 憑證信息
data請(qǐng)求體數(shù)據(jù)
filename文件名稱
action上傳路徑
onProgress上傳進(jìn)度回調(diào)
onSuccess上傳成功回調(diào)
onError上傳錯(cuò)誤回調(diào)

當(dāng)外部默認(rèn)不傳httRequest時(shí),會(huì)通過內(nèi)部封裝的ajax.js進(jìn)行上傳請(qǐng)求(內(nèi)部實(shí)現(xiàn)并沒有說什么復(fù)雜的地方,單純的實(shí)現(xiàn)原生XMLHttpRequest請(qǐng)求,這里就不對(duì)其內(nèi)容進(jìn)行討論可自行了解),需要注意的是當(dāng)自定義httpRequest時(shí),要對(duì)onProgress,onSuccess,onError進(jìn)行回調(diào),保證結(jié)果在內(nèi)部能獲取到正常響應(yīng)

// ??? upload.vue post
// upload.vue post方法
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);
  • 緩存當(dāng)前請(qǐng)求的每一個(gè)實(shí)例,同時(shí)在http-requestoptions內(nèi)的onSuccessonError回調(diào)時(shí),對(duì)緩存的請(qǐng)求實(shí)例進(jìn)行刪除.

??當(dāng)你使用自定義httpRequest注意點(diǎn):

有返回值時(shí),記得暴露abort方法,因?yàn)閮?nèi)部默認(rèn)ajax返回的實(shí)例是有abort方法可以中斷請(qǐng)求,而如果自定義時(shí)返回沒有abort方法時(shí),點(diǎn)擊刪除會(huì)導(dǎo)致報(bào)錯(cuò)

需要在自定義httpRequest內(nèi)部合理時(shí)機(jī)調(diào)用onSuccess,onProgress,onError,因?yàn)檫@是FileItem.status更新的時(shí)機(jī)

this.reqs[uid] = req;
if (req && req.then) {
    // 在最后觸發(fā)成功之后,再次調(diào)用回調(diào)
    req.then(options.onSuccess, options.onError);
}

?? Uploading

當(dāng)觸發(fā)onProgress回調(diào)時(shí),對(duì)FileItem對(duì)象的status設(shè)置為uploadingpercentage上傳進(jìn)度值,觸發(fā)外部監(jiān)聽的onProgress回調(diào)

// index.vue
handleProgress(ev, rawFile) {
  const file = this.getFile(rawFile);
  this.onProgress(ev, file, this.uploadFiles);
  file.status = 'uploading';
  file.percentage = ev.percent || 0;
}

?? Success

當(dāng)觸發(fā)onSuccess回調(diào)時(shí),對(duì)FileItem對(duì)象的status設(shè)置為success,添加response響應(yīng)值 ,與此同時(shí)觸發(fā)外部監(jiān)聽的onSuccess,onChange

 handleSuccess(res, rawFile) {
  const file = this.getFile(rawFile);
  if (file) {
    file.status = 'success';
    file.response = res;
    this.onSuccess(res, file, this.uploadFiles);
    this.onChange(file, this.uploadFiles);
  }
}

?? Fail

當(dāng)觸發(fā)onError回調(diào)時(shí),對(duì)FileItem對(duì)象的status設(shè)置為fail,與此同時(shí)觸發(fā)外部監(jiān)聽的onError,onChange,當(dāng)報(bào)錯(cuò)觸發(fā)handleError會(huì)對(duì)相應(yīng)報(bào)錯(cuò)的FileItemFileList中移除

handleError(err, rawFile) {
  const file = this.getFile(rawFile);
  const fileList = this.uploadFiles;
  file.status = 'fail';
  fileList.splice(fileList.indexOf(file), 1);
  this.onError(err, file, this.uploadFiles);
  this.onChange(file, this.uploadFiles);
}

?? Abort

由于組件對(duì)外提供了abort中斷請(qǐng)求的方法,可以通過傳入當(dāng)前正上傳的file對(duì)象或者文件的uid可中斷指定的文件上傳同時(shí)從FileList中移除,當(dāng)不傳任何參數(shù)時(shí)會(huì)對(duì)正在上傳的全部文件進(jìn)行中斷

// index.vue
// 對(duì)外暴露的方法
abort(file) {
      this.$refs['upload-inner'].abort(file);
},
// upload.vue
abort(file) {
  const { reqs } = this;
  // 傳了指定文件
  if (file) {
    // 這里支持傳入是一個(gè)uid
    let uid = file;
    if (file.uid) uid = file.uid;
    if (reqs[uid]) {
      // 將指定的請(qǐng)求進(jìn)行中斷
      reqs[uid].abort();
    }
  } else {
  // 不傳參數(shù)則將正在上傳的全部文件進(jìn)行中斷
    Object.keys(reqs).forEach((uid) => {
      if (reqs[uid]) reqs[uid].abort();
      delete reqs[uid];
    });
  }
}

事件觸發(fā)時(shí)機(jī)圖

總結(jié)

以上就是對(duì)Upload組件上傳流程的基本分析。不管是在對(duì)組件進(jìn)行二次開發(fā)或者單獨(dú)實(shí)現(xiàn)一個(gè)上傳組件,希望你能夠?qū)ζ淞鞒涕_發(fā)實(shí)現(xiàn)有更好的理解。 如果分析的不是很到位,希望能夠在評(píng)論留下你的想法和意見,你的評(píng)價(jià)和點(diǎn)贊是我學(xué)習(xí)和輸出的最大動(dòng)力??

以上就是ElementUI Upload源碼組件上傳流程解析的詳細(xì)內(nèi)容,更多關(guān)于ElementUI Upload組件上傳的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論