前端資源加載失敗優化


如何監控資源加載失敗

方案一:script onerror

我們可以給 script 標簽添加上 onerror 屬性,這樣在加載失敗時觸發事件回調,從而捕捉到異常。

<script onerror="onError(this)"></script>

 

並且,借助構建工具( 如 webpack 的 script-ext-html-webpack-plugin 插件) ,我們可以輕易地完成對所有 script 標簽自動化注入 onerror 標簽屬性,不費吹灰之力。

new ScriptExtHtmlWebpackPlugin({  custom: {    test: /\.js$/,    attribute: "onerror",    value: "onError(this)",  },});

 

方案二:window.addEventListener

上述方案已然不錯,但我們也試想是否可以減少 onerrror 標簽大量注入呢?類比腳本錯誤 onerror 的全局監控方式(詳見:腳本錯誤量極致優化-監控上報與 Script error),是否也可以通過 window.onerror 去全局監聽加載失敗呢?

答案否定的,因為 onerror 的事件並不會向上冒泡,window.onerror 接收不到加載失敗的錯誤。冒泡雖不行,但捕獲可以!我們可以通過捕獲的方式全局監控加載失敗的錯誤,雖然這也監控到了腳本錯誤,但通過 !(event instanceof ErrorEvent) 判斷便可以篩選出加載失敗的錯誤。

window.addEventListener(  "error",  (event) => {    if (!(event instanceof ErrorEvent)) {      // todo    }  },  true);

 

通過監控數據分析,我們發現現實情況不容樂觀。訪問頁面時存在資源加載失敗的情況超過了 10000 例/天,且隨着頁面訪問量的上升而增加。

另外,監控資源加載失敗的方式不止這些,上述兩種方式都屬於較好的方案,其他的方式就不再展開。

優化資源加載失敗

方案一:加載失敗時,刷新頁面(reload)

有了監控數據后,便可着手優化。當資源加載失敗時,刷新頁面可能是最簡單直接的嘗試恢復方式。於是當監控到資源加載失敗時,我們通過 location.reload(true) 強制瀏覽器刷新重新加載資源,並且為了防止出現一直刷新的情況,結合了 SessionStorage 限制自動刷新次數。

 

 

通過監控數據發現,通過自動刷新頁面,最終能恢復正常加載占異常總量 30%,優化比例不高,且刷新頁面導致了出現多次的頁面全白,用戶體驗不好。

方案二:針對加載失敗的文件進行重加載

替換域名動態重加載

只對加載失敗的文件進行重加載。並且,為了防止域名劫持等導致加載失敗的原因,對加載失敗文件采用替換域名的方式進行重加載。替換域名的方式可以采用重試多個 cdn 域名,並最終重試到頁面主域名的靜態服務器上(主域名被劫持的可能性小)

 

 

然而,失敗資源重加載成功后,頁面原有的加載順序可能發生變化,最終執行順序發現變化也將導致執行異常。

 

 

保證 JS 按順序執行

在不需要考慮兼容性的情況下,資源加載失敗時通過 document.write 寫入新的 script 標簽,可以阻塞后續 script 腳本的執行,直到新標簽加載並執行完畢,從而保證原來的順序。但它在 IE、Edge 卻無法正常工作,滿足不了我們項目的兼容性。

於是我們需要增加 “管理 JS 執行順序” 的邏輯。使 JS 文件加載完成后,先檢查所依賴的文件是否都加載完成,再執行業務邏輯。當存在加載失敗時,則會等待文件加載完成后再執行,從而保證正常執行。

 

 

手動管理模塊文件之間的依賴和執行時機存在着較大的維護成本。而實際上現代的模塊打包工具,如 webpack ,已經天然的處理好這個問題。通過分析構建后的代碼可以發現,構建生成的代碼不僅支持模塊間的依賴管理,也支持了上述的等待加載完成后再統一執行的邏輯。

// 檢查是否都加載完成,如是,則開始執行業務邏輯
function checkDeferredModules() {
// ...
if (fulfilled) {
// 所有都加載,開始執行
result = __webpack_require__((__webpack_require__.s = deferredModule[0]));
}
}

 

 

 

然而,在默認情況下,業務代碼的執行不會判斷配置的 external 模塊是否存在。所以當 external 文件未加載完成或加載失敗時,使用對應模塊將會導致報錯。

"react":  (function(module, exports) {     
  eval("(function() { module.exports = window[\"React\"]; }());");
})

 

所以我們需要在業務邏輯執行前,保證所依賴的 external 都加載完成。最終通過開發 wait-external-webpack-plugin webpack 插件,在構建時分析所依賴的 external,並注入監控代碼,等待所有依賴的文件都加載完成后再統一順序執行。(詳見:Webpack 打包后代碼執行時機分析與優化)

至此,針對加載失敗資源重試的邏輯最終都通過構建工具自動完成,對開發者透明。重試后存在加載失敗的情況優化了 99%。減少了大部分原先加載失敗導致異常的情況。

始終加載失敗該怎么辦

用戶網絡千變萬化,或臨時斷網、或瀏覽器突然異常,那些始終加載失敗的情況,我們又該如何應對呢? 一個友好的提醒彈框或是最后的稻草,避免用戶的無效等待,緩解用戶感受。

 

 

總結

以上,便是對資源加載失敗優化的整體方案,從如何監控加載失敗、加載失敗時重試、重試失敗后的提醒等方面。大幅優化修正了加載失敗的問題,也緩解着實遇到異常的用戶使用體驗。

如有不妥,懇請斧正,謝謝。

轉自https://mp.weixin.qq.com/s/0JMLZYgNAiyrHmzPBu5rYw

喜歡這篇文章?歡迎打賞~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM