【前言】
小程式開發中,有時會遇到下面這種情況,專案目錄中存放過大的js包,會被警告影響手機端效能,同時讓開發編譯啟動變得很慢。慢是其次,單是影響效能這一點,就需要解決一下。
【雲資源】
將專案js包放入公司的oss、obs之類的雲端儲存上,透過https連結來訪問。
https連結不能使用node的require載入,會拋錯,但是可以透過其他兩種方式進行訪問:
1. request請求
2. fileSystemManager檔案管理器
方法1簡單,但是不可取,原因如下:
1) 獲得的檔案資訊,沒有較好的儲存方法,既不能存在store中,也不能存在local中,不僅是資料儲存格式問題,更重要的同樣會造成效能缺點,總不能解決了一個問題,又創造新的問題
2) 由於1)描述的那樣,如果不能儲存檔案,那麼每次需要依賴這個檔案時,就需要每次請求介面,這就造成了資源浪費
故此,基於以上兩點,只能負重前行,選擇較為麻煩和讓人頭疼的方案2(fileSystemManager檔案管理器),雖然麻煩,但是卻可以一勞永逸,並且可以抽離邏輯封裝成一套方法,可以在以後複用。
【前期準備】
實現方案前,有幾個注意事項:
1. 首先要將存放雲資源的oss或obs的域名配置在白名單中,這裡就需要配置request合法域名和downloadFile合法域名。
2. 需要勾選“不校驗合法域名...”,不勾選的話,真機會遇到意想不到的問題。
3. 將js檔案轉成json檔案,如果沒辦法,就自行抽離js包,拆出一個json檔案,因為微信fileSystemManager不支援讀取js檔案,js檔案會變成string文字,但是支援json。
當然,如果你覺得可以使用JavaScript 直譯器來破這個局,那麼你又一次要碰壁了,微信官方對此做了限制,禁止eval5、estime、evil-eval等動態執行程式碼的js直譯器。
原文地址:關於禁止小程式JavaScript直譯器使用規範要求
【方案思考】
fileSystemManager,它是getFileSystemManager返回的物件,給我們暴露出了多個方法,下面為部分截圖
原文地址:FileSystemManager
為保證效能和可靠性,這裡我們採用下面這種方案:
首次:下載 + 儲存 + 讀取,非首次:直接讀取
流程圖如下:
【方法封裝】
我這裡提供了兩種封裝寫法:
- 鏈式寫法,方便回撥處理,不需要回撥可以採用寫法2
- 解耦式寫法,降低了函式顆粒度,每個方法獨立,更加靈活,可以單獨使用某一個函式
千言萬語,前面的鋪墊已完成,直接上程式碼:
鏈式寫法
/** * 讀取靜態資源,如果沒有該資源,則觸發下載,並儲存至使用者本地 * 注意:此方法為鏈式呼叫,如果需要單獨的方法,可選擇下面拆分過的獨立方法 * @param {String} name 檔名 -必傳 * @param {String} url 檔案路徑 -必傳 * @param {Function} callback 是否需回撥操作 -非必傳 */ export async function getSystemFileInfoToChain(name = 'jn', url, callback = null) { if (!url) { return; } const fileManager = uni.getFileSystemManager(); // 使用者檔案路徑 let filePath = `${uni.env.USER_DATA_PATH}/${name}`; // 讀取靜態資源,如果沒有該資源,則觸發下載 try { let res = await fileManager.readFileSync(filePath, 'utf-8'); const resData = res && JSON.parse(res); let resFile = await fileManager.readFileSync(url, 'utf-8'); console.log('🆒 fileManager 111', resData, name, url, resFile); if (callback && typeof callback === 'function') { callback(resData); } // 暴露出檔案內容 return resData; } catch (err) { console.log('getSystemFileInfoToChain err', err); // 下載檔案至本地 try { const res = await uni.downloadFile({ url }); let tempFilePath = res?.[1]?.tempFilePath; console.log('🆒 fileManager 222', tempFilePath); if (!tempFilePath) { return; } // 儲存檔案至本地 try { await fileManager.saveFileSync(tempFilePath, filePath); console.log('🆒 fileManager 333', url); // 再次觸發,獲得需要的資料 getSystemFileInfoToChain(name, url, callback); } catch (err) { console.log('saveFileSync err', err); } } catch (err) { console.log('downloadFile err', err); } } }
解耦式寫法
/** * 讀取靜態資源,如果沒有該資源,則觸發下載,並儲存至使用者本地 * @param {String} name 檔名 -必傳 * @param {String} url 檔案路徑 -必傳 * @param {Boolean} next 是否下一步(如果讀取失敗,則下載檔案至本地) -非必傳 */ export async function getSystemFileInfo(name = 'jn', url, next = false) { const fileManager = uni.getFileSystemManager(); // 使用者檔案路徑 let filePath = `${uni.env.USER_DATA_PATH}/${name}`; try { let res = await fileManager.readFileSync(filePath, 'utf-8'); const resData = res && JSON.parse(res); console.log('🆒 fileManager 111', resData); // 暴露出檔案內容 return resData; } catch (err) { // 允許下一步(下載檔案),且有url,進行下載儲存 next && url && onDownloadFile(name, url, next); console.log('readFileSync err', err); } // console.log('🆒 fileManager', fileManager, uni.env.USER_DATA_PATH); } /** * 下載檔案至本地 * @param {String} name 檔名 -必傳 * @param {String} url 檔案路徑 -必傳 * @param {Boolean} next 是否下一步(檔案內容讀取) -非必傳 */ export async function onDownloadFile(name = 'jn', url, next = false) { if (!url) { return; } try { const res = await uni.downloadFile({ url }); let tempFilePath = res?.[1]?.tempFilePath; console.log('🆒 fileManager 222', tempFilePath); onSaveFile({ name, url }, tempFilePath, next); } catch (err) { console.log('downloadFile err', err); } } /** * 儲存檔案至本地 * @param {String} params[0].name 檔名 -必傳 * @param {String} params[0].url 檔案路徑 -非必傳 * @param {String} tempFilePath 臨時檔案路徑 -必傳 * @param {Boolean} next 是否下一步(檔案內容讀取) -非必傳 */ export async function onSaveFile({ name = 'jn', url }, tempFilePath, next = false) { if (!tempFilePath) { return; } const fileManager = uni.getFileSystemManager(); // 使用者檔案路徑 let filePath = `${uni.env.USER_DATA_PATH}/${name}`; try { await fileManager.saveFileSync(tempFilePath, filePath); // 允許下一步(檔案內容讀取),且有url,進行檔案內容讀取 next && url && getSystemFileInfo(name, url, false); console.log('🆒 fileManager 333', url); } catch (err) { console.log('saveFileSync err', err); } }
到此,就封裝完成了,後面使用看具體場景,來選擇鏈式、解耦式寫法。