Как обрабатывать развертывания с разделением кода в Webpack?

Вот неожиданная проблема, с которой я столкнулся при расщеплении кода в Webpack: представьте себе этот сценарий:

  1. Пользователь загружает приложение React с разбивкой кода в Webpack и загружает несколько пакетов
  2. Развертывание происходит, и содержимое любых будущих блоков, которые пользователь может получить с сервера, обновляется (примечание: предыдущие блоки удаляются на сервере во время развертывания)
  3. Пользователь нажимает на ссылку и загружает новый маршрут, который запускает загрузку дополнительных блоков. За исключением того, что эти новые чанки несовместимы с теми, которые уже загружен браузером пользователя, и приложение разрывается из-за ошибки времени выполнения

Как можно предотвратить этот сценарий?

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

Если используется preload-webpack-plugin, все фрагменты могут быть предварительно выбраны, но они будут оставаться в кэше только в течение короткого времени (5 минут в Chrome).

4 ответа

Эта проблема очень хорошо сформулирована.

Я добавлю, что «Удаление» может быть неправильным названием для происходящего, в зависимости от настроек.

Первым моим ответом на эту проблему было то, что это проблема кеширования. Эти старые файлы фрагментов были взяты вместо нового. Это близко к тому, что происходило, по крайней мере, в моем случае у меня было следующее:

index.js

      const Page1 = lazy(() => import('./page/Page1'));
const Page2 = lazy(() => import('./page/Page2'));

const main = () => {
  {
    '/page1': Page1,
    '/page2': Page2,
  }[window.location.href](); /* Some Render Router Implementation */
};
  1. Версия 1, развернутая в (https: // my-domain / distribution_folder / *)
  2. Пользователь загрузит V1
  3. Версия 2, развернутая в (https: // my-domain / distribution_folder / *)
  4. Пользователь (который не обновлялся) динамически загружал фрагментированный маршрут, используя свой кэшированный файл V1.
  5. Запрос будет отправлен на (https: // my-domain / distribution_folder / {page_name}. {Chunk_hash} .js)
  6. Произойдет ошибка фрагмента, потому что этого уникального фрагмента больше не будет.

Это интересно, потому что используемый провайдер переносил трафик на новую версию. Поэтому я подумал, что на этом все закончится, но я не понимал, что любой пользователь все еще может использовать ранее развернутую версию - как они узнают? Они уже используют приложение. Браузер уже скачал приложение ().

Решение действительно зависит от того, куда вы динамически импортируете эти фрагменты. В приведенном выше случае, поскольку они являются маршрутами страниц, мы можем выполнить жесткое обновление, когда пользователь запрашивает другую страницу, когда мы не можем найти фрагмент. Однако это предполагает, что ваш Cache-Controlхотя заголовки настроены правильно. Например:

  • -> Cache-Control: no-store
  • page/{page_name}.{chunk_hash}.js -> Cache-Control: public,max-age=31536000,immutable

Мы можем сделать эти блоки неизменяемыми, потому что иногда они не меняются между выпусками, и если они не меняются, почему бы не использовать кешированную версию. Тем не мение, index.jsне могут быть сохранены в кеше, потому что это «маршрутизатор», который динамически загружает контент, и это всегда будет меняться.

Плюсы

  • Больше нет ошибок загрузки фрагментов
  • Нам не нужно загружать все при загрузке первой страницы
  • Меньше сложности за счет отсутствия обслуживающего работника

Минусы

  • Этот подход требует обновления для пользователей

Связанные вопросы

Как пишет Макс Стойбер на spectrum.chat:

ServiceWorkers очень полезны при разделении кода!

Мы используем отличный offline-плагин от @nekr для локального кэширования всех текущих пакетов, поэтому независимо от того, обновляет ли сервер файлы или нет, ServiceWorker всегда будет обслуживать файлы из локального кэша. Каждый час он будет проверять сервер на наличие обновлений и, если обновление доступно, загружать все свежие пакеты с удаленного сервера и кэшировать их локально. В следующий раз, когда пользователь перезапустит приложение, будет использоваться новая версия приложения!

https://github.com/NekR/offline-plugin

Это решение означает, что ваше приложение загружает все чанки заранее, что лишает цели разделения кода с точки зрения пропускной способности, но, по крайней мере, вы все равно сохраняете преимущество только разбора чанков, необходимых для загрузки приложения, что для меня важно для медленные устройства. Кроме того, обновление / кэширование браузера теперь включает жизненный цикл Service Worker (см. "Ожидание" на https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle).

Если имена файлов чанков хэшированы, разве старая ссылка на маршрут не соединится со старым хэшированным чанком (который, вероятно, все еще будет доступен) и загрузит все нормально?

https://webpack.js.org/guides/caching/

Простой способ убедиться, что браузер забирает измененные файлы, - это использовать замены output.filename. Подстановка [hash] может использоваться для включения в имя файла хеша, специфичного для сборки, однако еще лучше использовать подстановку [chunkhash], которая включает хеш, специфичный для фрагмента, в имени файла.

Другие вопросы по тегам