Оптимизация представлений Express React для первой загрузки; CI/CD

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

Другой, менее очевидный, — плохая производительность при первой загрузке.

Проблема

ERV использует @babel/register, чтобы сначала скомпилировать дерево React, прежде чем преобразовать его в строку, которая будет служить ответом. Однако вместо предварительной компиляции этот код использует хук babel require для компиляции на лету. Это отлично подходит для простоты. Все, что вам нужно сделать, чтобы реализовать это, это потребовать его в вашем коде. Затем он скомпилирует все, что придет позже. Недостатком является то, что, поскольку это делается на лету, компиляция не происходит до тех пор, пока этот конкретный файл не будет загружен в память. Это приводит к значительной задержке времени выполнения во время компиляции исходного кода.

Когда дело доходит до непрерывной доставки, это определенно проблема!

Но что мы можем с этим поделать?

Ну, мы на самом деле рассмотрели пару вариантов...

В Indigo мы понимаем, что будущее за Docker! Любое новое приложение, которое мы создаем, должно запускаться в контейнере. В связи с этим развертывание изменений в рабочей среде представляет собой простую задачу развертывания новых контейнеров для замены существующих. Мы могли бы просто добавить дополнительный шаг в наш процесс развертывания, когда мы либо вручную, либо каким-то автоматическим образом делаем запрос к конечной точке, чтобы принудительно выполнить компиляцию. После компиляции результирующий пакет сохраняется в памяти и используется при каждом последующем запросе, что приводит к гораздо более быстрому времени загрузки.

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

Другой вариант, который, как мы решили, нам подходит, — предварительно скомпилировать дерево React. Это включает в себя запуск инструмента сборки, такого как webpack, перед запуском сервера для создания пакета JS. Затем ссылайтесь на скомпилированный пакет в ERV вместо исходного кода. Мы выбрали этот вариант по нескольким причинам. Во-первых, он хорошо масштабируется. Изменения кода будут применяться к любому новому контейнеру, который мы запускаем. И во-вторых, мы уже используем webpack для создания нашего клиентского пакета. Это означает, что большая часть работы уже сделана!

Решение

Настройка веб-пакета

К счастью, webpack позволяет использовать несколько конфигураций путем экспорта массива. Нам просто нужно уйти от чего-то подобного…

Что-то вроде этого…

Для конфигурации сервера не забудьте установить target = 'node' и output.libraryTarget = 'commonjs2'. Второй особенно важен, потому что он добавляет module.exports к самому пакету, чтобы его можно было «требовать» в другом месте. ERV должен требовать пакет, чтобы обслуживать его как HTML.

Выполнение веб-пакета со второй конфигурацией выше приведет к 2 пакетам. 1 для клиента, который выводится в нашу общедоступную папку JS ./public/javascripts, и 1 для нашего сервера, который выводится в ./dist.

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

Настройка представлений Express React

Здесь нужно внести несколько изменений, но все они довольно просты.

  1. Обновите путь «представления» к папке серверного пакета dist.
  2. Обновите суффикс нашего механизма просмотра, чтобы он соответствовал файлу пакета, сгенерированному webpack («js»).
  3. Установите для флага transformViews значение false и удалите любую конфигурацию babel (если есть) из нашей конфигурации ERV.

В зависимости от того, как вы настроили свою конфигурацию, могут быть другие изменения, которые вам нужно будет внести здесь, но 3 выше применимы к большинству.

…и это все!

Довольно легко, да?

Результаты

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

Следующие шаги

Для нас основной причиной внедрения ERV в первую очередь была низкая опция конфигурации для SSR. Теперь, когда у нас есть веб-пакет, обрабатывающий серверный пакет, нам больше не нужен ERV. Следующим логическим шагом для нас является его полное удаление и замена простой реализацией ReactDOMServer.renderToString() или ReactDOMServer.renderToNodeStream().

Единственная причина, по которой мы не делаем этого сразу, заключается в том, что в настоящее время весь наш HTML, даже тот, который является статическим, написан на React и скомпилирован в HTML с помощью ERV. Статическая, не относящаяся к React часть кодовой базы потребует некоторого рефакторинга, чтобы полностью удалить ERV.