Прерывание горячей перезагрузки и ее исправление

После того, как я присоединился к Zoover в марте, одной из первых вещей, которые мы сделали, было добавление горячей перезагрузки в наше приложение React с помощью Hot Module Reloading Webpack и react-hot-loader. До этого мы использовали gulp-livereload и его обновление на всю страницу, чтобы получить обновленный интерфейс. Первоначально он работал достаточно хорошо, но по ходу дела он стал более ненадежным, и после того, как мы добавили разделение кода и react-loadable в наше приложение, дело дошло до того, что мои коллеги просто выполняли обновление всей страницы вручную.

Это довольно плохое место: неуверенность в правильности горячего обновления в основном убивает все преимущества, которые у него могут быть: во-первых, вы будете ждать, пока не увидите обновление экрана; тогда вы все равно обновитесь, потому что вам нужно быть уверенным, что увиденное правильно. Честно говоря, нам было лучше с обновлением полной страницы, которое, по крайней мере, было автоматическим. Однако у этого были некоторые недостатки: наша настройка рендеринга на стороне сервера означает две сборки Webpack, время перестройки ›1 с и сервер, который не работает, когда происходит полное обновление страницы, что часто означало, что для просмотра изменений на вашем компьютере требуется 5–10 секунд. экран.

Почему

Первое предположение заключалось в том, что разбиение кода в сочетании с react-loadable нарушило его.

Если вы не знаете, что это за две вещи: разделение кода - это возможность определять точки разделения в вашем коде . Точка разделения - это точка, в которой Webpack разделяет ваш пакет на отдельные файлы, которые могут быть загружены по запросу. Это может помочь вам снизить вес ваших критически важных ресурсов, и это абсолютно необходимо для более крупных приложений, особенно для мобильных устройств (где размер пакета больше всего вредит). react-loadable - это довольно изящный компонент React, который помогает вам импортировать и отображать лениво загружаемые компоненты, а также заставляет все это работать на сервере (что нетривиально).

Это первое предположение поначалу казалось неверным: в некоторых (или во многих) случаях горячая перезагрузка нарушалась одним из плагинов оптимизации React, которые мы использовали (включение этих плагинов в режиме разработки, вероятно, было не лучшим выбором, но, увы). Они модифицировали компоненты таким образом, что react-hot-loader работать было затруднительно. После отключения этих плагинов горячая перезагрузка, безусловно, работала лучше, но она все еще не работала для компонентов с ленивой загрузкой. Сначала я думал, что это как-то связано с тем, что модуль не требуется из-за react-loadable работы, но оказалось, что он (также) ломается на уровне Webpack.

Для тех, кто не знаком с горячей перезагрузкой в ​​контексте Webpack и React: вам нужно «ловить» горячие обновления для конкретного модуля в том же месте, где вы рендерит свое приложение. Если вы получили горячее обновление, вам нужно будет повторно выполнить рендеринг приложения. Между тем, react-hot-loader добавляет небольшой фрагмент кода к каждому компонентному модулю, который регистрирует этот компонент с помощью react-hot-loader. Затем, когда вы повторно визуализируете свое приложение, react-hot-loader прокси React.createElement вызывает и заменяет компоненты React на компоненты из собственного реестра, в котором будут последние версии из горячего обновления.

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

Исправление

Есть несколько способов заставить его работать:

  • Явно принимайте обновления для всех фрагментов в вашей точке входа и повторно визуализируйте эти обновления.
  • Используйте наличие module.hot или process.env !== 'production', чтобы добавить явные статические require операторы для лениво загружаемых модулей.

Однако оба варианта по существу требуют двойного администрирования. Я ленив, поэтому не хочу делать что-то более одного раза (мне нужно сэкономить время, чтобы компенсировать весь синий экран смертей, который дает мне мой XPS). Я хочу отключить разделение кода Webpack в режиме разработки. Звучит довольно просто, правда? Что ж, оказывается, это сложнее, чем я думал. webpackMode: eager может решить наши проблемы, но мы можем указать это только в комментариях для каждого импорта, а не для всего сразу. ДобавлениеLimitChunkCountPlugin и установка maxChunks: 1 отключает вывод нескольких файлов, но, похоже, ничего не решает. После того, как я немного потянул за волосы, я подумал, что может быть компромисс: может быть, мы сможем добавить явные операторы require автоматически?

На очереди плагины Babel: идеальное место для синтаксического анализа исходного кода и автоматического добавления некоторых операторов, предотвращающих двойное администрирование, которого мы пытаемся избежать. Достаточно сложно найти документацию о том, как написать плагин Babel, кроме babel-handbook и некоторых примеров плагинов, но начать работу с ним действительно легко, как только вы запустите AST explorer.

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

Попробуй сам:

Чтобы использовать его, просто добавьте плагин в свой .babelrc файл:

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

О Zoover

Zoover - это туристический сайт, расположенный в Амстердаме. Наша цель - помочь пользователям найти лучший отпуск. Наш веб-сайт построен на React на серверной части Node, и мы всегда ищем людей, которые хорошо разбираются в своем деле.