Повторная загрузка фрагментов кода для восстановления после сбоев сети.

Мое путешествие

После того, как я разделил свой одностраничный пакет приложений на несколько частей с динамическим импортом и отложенной загрузкой React, я начал время от времени замечать ошибки в производственной среде. Что-то вызывало ошибку времени выполнения, которая приводила к сбою всего приложения и достижению моей границы ошибки React. Просматривая логи, ошибка была такая:

Error: ChunkLoadError: Loading chunk 0 failed.

Основная причина

Покопавшись, я понял, что основная причина была в сети. Больше чанков означало больше запросов, что означало больше шансов сбоя сети. Если какой-либо из запрошенных фрагментов вышел из строя, возникнет указанная выше ошибка. Но я ничего не мог сделать, чтобы исправить пользовательскую сеть. Итак, что будет лучше всего?

Подход

Что мне делать, если веб-страница не загружается? Обновляю страницу и пробую еще раз. Тот же принцип можно применить к фрагментам кода. Если произошел сбой, снова загрузите проблемный фрагмент и надейтесь, что это удастся. Хотя это, конечно же, не гарантирует решения, но это лучший способ решить что-то вне нашего контроля.

Chunk Retry

Итак, как это делается в коде? Разделение кода достигается за счет синтаксиса динамического импорта. Это возвращает обещание, которое выполняется, когда блок успешно загружен. Вместо того, чтобы напрямую передавать обещание динамического импорта в React lazy, нам нужна оболочка, реализующая механизм повтора.

// App.js
import importRetry from './importRetry';
const Chunk = lazy(() =>
  importRetry(() => import('./Chunk'))
);
// importRetry.js
async function importRetry(importFn, retries = 2, interval = 1000) {
  try {
    return await importFn();
  } catch (error) {
    if (retries) {
      await wait(interval);
      return importRetry(importFn, retries - 1, interval);
    } else {
      throw new Error(error);
    }
  }
}
function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Обертка importRetry попытается выполнить динамический импорт. В случае неудачи он подождет некоторое время и попытается снова, до числа, указанного в retries. Надеюсь, подождав, проблема с сетью будет решена. Если после всех повторных попыток запрос все еще не был успешным, у нас нет другого выбора, кроме как вернуть ошибку.

Webpack

Если вам нужен более автоматизированный способ обработки повторных попыток динамического импорта, для этого есть пакет npm - webpack-retry-chunk-load-plugin обеспечивает ту же функциональность, но через Webpack. Он предоставляет плагин, который автоматически вводит код для перезагрузки блока в случае сбоя.

// webpack.config.js
const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin');
module.exports = {
  plugins: [
    new RetryChunkLoadPlugin({
      maxRetries: 3,
    })
  ];
};

Этот плагин обеспечивает меньшую гибкость, чем прямая реализация повторных попыток через код, но он гарантирует, что каждый импорт будет иметь механизм повторных попыток.

Последние мысли

По мере того как мое одностраничное приложение становилось все больше и больше, разделение кода стало необходимым инструментом для обеспечения хорошего времени загрузки. Но с плохими маршрутизаторами, пятнистыми соединениями и солнечными вспышками следует ожидать сетевых сбоев на этих фрагментах кода. Мы должны сделать все возможное, чтобы наша кодовая база была устойчивой к ошибкам и пыталась самовосстановиться, когда они действительно возникают. В этом случае мы должны снова попытаться загрузить чанк.

Ресурсы