<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      解決 Semi Design Upload 組件實現(xiàn)自定義壓縮,上傳文件后無法觸發(fā) onChange

      背景

      我們團隊主要在做 C 端產(chǎn)品,對于 C 端應用,圖片資源使用 CDN 十分重要,因此我們曾建立了一個文件上傳平臺:上傳文件后,可以復制圖片的 CDN URL 在前端項目中使用。

      目前服務端不會對圖片做壓縮,使用前得先借助其他工具手動壓縮再上傳,體驗很差。調(diào)研后發(fā)現(xiàn),純前端就能完成壓縮且完全滿足需求,于是決定給 Upload 組件加上「自動壓縮」能力。

      實現(xiàn)思路

      整體流程:

      1. 用戶多選文件 → 文件列表展示文件名、狀態(tài)、預覽圖等
      2. 異步批量壓縮圖片,實時更新狀態(tài): 壓縮中 → 已壓縮,等待上傳 / 壓縮失敗,使用原文件
      3. 用戶點擊「開始上傳」→ 狀態(tài)變?yōu)?上傳中 → 上傳成功 / 失敗

      Semi Design 的 Upload 組件有兩處可切入壓縮邏輯:

      方案 入口 說明
      transformFile 選中后、上傳前觸發(fā),返回新 FileItem
      onChange 文件狀態(tài)變化回調(diào),可完全自定義流程

      需要展示「壓縮中」「壓縮失敗」等中間態(tài),因此選用更靈活的 方案②

      關鍵類型

      Upload 組件的 FileItem 類型包含了一些信息,因此我通過更新 FileItem 對象的屬性可以很容易實現(xiàn)文件列表的狀態(tài)更新。

      interface FileItem {
          event? : event,  // xhr event
          fileInstance?: File, // original File Object which extends Blob, 瀏覽器實際獲取到的文件對象(https://developer.mozilla.org/zh-CN/docs/Web/API/File)
          name: string,
          percent? : number, // 上傳進度百分比
          preview: boolean, // 是否根據(jù)url進行預覽
          response?: any, // xhr的response, 請求成功時為respoonse body,請求失敗時為對應 error
          shouldUpload?: boolean; // 是否應該繼續(xù)上傳
          showReplace?: boolean, // 單獨控制該file是否展示替換按鈕
          showRetry?: boolean, // 單獨控制該file是否展示重試按鈕
          size: string, // 文件大小,單位kb
          status: string, // 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait';
          uid: string, // 文件唯一標識符,如果當前文件是通過upload選中添加的,會自動生成uid。如果是defaultFileList, 需要自行保證不會重復
          url: string,
          validateMessage?: ReactNode | string,
      }
      

      代碼實現(xiàn)

      <Upload
          // other props
          customRequest={customRequest}
          fileList={storeFileList}
          onChange={onChange}
        />
      

      傳入 fileList 表示組件處在受控模式。

        const onChange = async ({ fileList }: OnChangeProps) => {
          const newFileList = uniqBy(fileList, (file) => file.name);
          actions.batchUpdate({ fileList: newFileList });
      
          const filesToCompress = getFilesToCompress(newFileList);
      
          if (filesToCompress.length === 0) {
            return;
          }
      
          for (const file of filesToCompress) {
            updateFileList(file.name, {
              status: "validating",
              validateMessage: "壓縮中...",
            });
          }
      
          const compressionPromises = files.map(async (file) => {
            try {
              const compressedFile = await compressImage(file, 80);
      
              updateFileList(file.name, {
                fileInstance: compressedFile,
                status: "wait",
                validateMessage: "已壓縮,等待上傳",
              });
            } catch {
              updateFileList(file.name, {
                status: "wait",
                validateMessage: "壓縮失敗,使用原文件",
              });
            }
          });
      
          await Promise.allSettled(compressionPromises);
        };
      

      iShot_2025-10-22_16.31.44

      為了演示效果,compressImage 里故意 sleep 了 2s。

      踩坑:onSuccess 后 onChange 未觸發(fā)

      文件上傳成功后,我們在 customRequest 里調(diào)用 onSuccess 期望組件自動把狀態(tài)改為 success,但 onChange 居然沒執(zhí)行!

      const customRequest = ({ file, onProgress, onError, onSuccess }) => {
        batchUpload(file.fileInstance)
          .then(res => {
            if (res.data.code === '0') onSuccess(res, file.event!);
            // 其他邏輯
          })
          .catch(onError);
      };
      

      排查過程

      image

      經(jīng)過幾輪友好 AI 對話,它總往錯誤方向跑,即使我告訴它去源碼中尋找答案。看來只能動動小手了。

      semi 有兩個概念:

      • Foundation:Foundation 包含最能代表 Semi Design 組件交互的業(yè)務邏輯,包括 UI 行為觸發(fā)后的各種計算、分支判斷等邏輯,它并不直接操作或者引用 DOM,任意需要 DOM 操作,驅(qū)動組件渲染更新的部分會委派給 Adapter 執(zhí)行。
      • Adapter:Adapter 是一個接口,具有 Foundation 實現(xiàn) Semi Design 業(yè)務邏輯所需的所有方法,并負責 1. 組件 DOM 結構聲明 2.負責所有跟 DOM 操作/更新相關的邏輯,通常會使用框架 API 進行 setState、getState、addEventListener、removeListener 等操作。適配器可以有許多實現(xiàn),允許與不同框架的互操作性。

      所以我們可以直接去 upload/foundation.ts 中查看實現(xiàn)邏輯。

      image

      handleSuccess 中有調(diào)用 notifyChange, notifyChange 就是 props.onChange

      image

      我們還看到在handleSuccess 中有一個判斷, 如果未找到文件就 return, 也就不會調(diào)用 onChange 了。我們繼續(xù)查看判斷中用到的 _getFileIndex 函數(shù)

      image

      它是通過對比 File 對象的 uid 屬性實現(xiàn)的,瀏覽器原生 File 對象是沒有 uid 屬性的,那么這個 uid 是哪里來的呢?也是 upload/foundation.ts 中添加的。

      image

      到此,我們可以大膽猜測,我們在上傳文件后,調(diào)用 onSuccess,走到 handleSuccess 邏輯被提前 return 了,原因就是上傳的文件對象中沒有 uid 或者 uid 不一致。我們在代碼中斷點看一下:

      image

      的確,F(xiàn)ile 對象中并沒有 uid 字段。 我們把壓縮邏輯注釋掉,繼續(xù)斷點并拿 fileInstance 對比下:

      image

      對比得知,壓縮后的 File 對象中沒有 uid 字段,這證實了我們之前的猜想。排查過程總結如下:

      1. 翻 Semi 源碼(upload/foundation.ts) handleSuccessnotifyChange(props.onChange),但前面有一行:“找不到文件就提前 return”。
      2. _getFileIndex 通過 uid 匹配文件。原生 File 對象沒有 uid,是 Semi 在添加文件時動態(tài)掛上去的。
      3. 我們壓縮后把 fileInstance 整個替換成了新 File,導致 uid 丟失,于是 _getFileIndex 返回 -1 → 直接 return → onChange 永遠不會觸發(fā)。

      解法:把 uid 還回去

      解決這個問題非常簡單, 由于 uid 是本來就有的,只是我們對文件壓縮后給丟了, 所以文件壓縮后再還回去就可以了。

      image

      iShot_2025-10-22_17.46.40

      posted @ 2025-10-22 18:10  guangzan  閱讀(136)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 日本一区二区三区后入式| 午夜DY888国产精品影院| 国产一区二区亚洲一区二区三区 | 搡老熟女老女人一区二区 | 国产成人无码免费视频在线| 欧美成人h精品网站| 激情综合网激情综合网激情| 一本大道久久香蕉成人网| 玩弄美艳馊子高潮无码| 一本久久a久久精品综合| 国产精品爆乳奶水无码视频免费 | 麻豆最新国产AV原创精品| 天堂а√8在线最新版在线| 一级女性全黄久久片免费| caoporn成人免费公开| 欧美综合天天夜夜久久| 亚洲天堂av免费在线看| 国产精品黄大片在线播放| 国内精品久久人妻无码不卡| 国产一二三五区不在卡| 情欲少妇人妻100篇| 精品国产亚洲区久久露脸| 风韵丰满妇啪啪区老老熟女杏吧| 亚洲综合av男人的天堂| 日韩亚洲精品中文字幕| 国产精品久久久久久久久人妻| 亚洲成人av在线资源网| 亚洲av无码精品色午夜| 四虎成人在线观看免费| 人妻少妇精品系列| 九九热视频在线免费观看| 国产永久免费高清在线| 人妻激情乱人伦视频| 丁香五月婷激情综合第九色 | 昭通市| 日韩中文字幕有码午夜美女| 久久SE精品一区精品二区| 超碰人人模人人爽人人喊手机版| yw尤物av无码国产在线观看| 国产初高中生粉嫩无套第一次| 精品久久人人做爽综合|