DevGang
Авторизоваться

Ускорение работы приложения Angular с помощью esbuild

Компания Cardiologs создает приложения, которые позволяют медицинским работникам экономить время на анализ электрокардиограммы (ЭКГ) благодаря искусственному интеллекту.

Мы разрабатываем SaaS-предложения, построенные на следующем технологическом стеке:

Недавно мы создали новый продукт с использованием React и Vite, и мы были потрясены тем, насколько Vite улучшает работу разработчиков, ускоряя время сборки.

Наше приложение на Angular довольно большое: 259 компонентов, 47 модулей, 800 тысяч строк кода. По сравнению с нашим стеком React оно кажется медленным на старте, и это как раз то, что мы давно хотели улучшить.

Начиная с v17 команда Angular добавила поддержку esbuild: https://angular.io/guide/esbuild.

Давайте попробуем перейти с Webpack на esbuild и посмотрим, что это изменит для нас в плане производительности.

Переведено с помощью DeepL.com (бесплатная версия)

Что нужно было изменить

Мы используем Nx, и включение esbuild начинается с изменения исполнителя сборки в файле project.json:

  "build": {
-      "executor": "@angular-builders/custom-webpack:browser",
+      "executor": "@angular-devkit/build-angular:browser-esbuild",
    }

Это нарушит некоторые моменты, вот что нам пришлось изменить:

Импорт ESM по умолчанию

Мы импортируем несколько модулей CommonJS, которые не поддерживают синтаксис модулей ES6, например momentjs:

▲ [WARNING] Calling "moment" will crash at run-time because it's an import namespace object, not a function [call-import-namespace]

  Consider changing "moment" to a default import instead:

    libs/help/src/lib/help/manual/manual.component.ts:5:7:
      5 │ import * as moment from 'moment';
        │        ~~~~~~~~~~~
        ╵        moment

Эту ошибку можно исправить, добавив этот флаг в tsconfig.json:

  {
    "compilerOptions": {
+     "esModuleInterop": true,
    }
  }

А затем измените все стандартные импорты на что-то вроде этого:

-  import * as moment from 'moment';
+  import moment from 'moment';

Мы также постепенно отказываемся от moment в пользу date-fns в нашей кодовой базе, чтобы решить проблемы с размером пакета в долгосрочной перспективе.

См. https://angular.io/guide/esbuild#esm-default-imports-vs-namespace-imports

Переменные окружения Node с Webpack

Наша сборка Webpack использовала конструктор @angular-builders/custom-webpack, чтобы получить переменные окружения во время сборки и передать их браузеру:

function getClientEnvironment() {
  // Grab NX_* environment variables and prepare them to be injected
  // into the application via DefinePlugin in webpack configuration.
  return {
    'process.env': Object.keys(process.env)
     .reduce((env, key) => {
       if (/^NX_/i.test(key)) {
         env[key] = JSON.stringify(process.env[key]);
       }
       return env;
    }, {});
  };
}

Затем во время сборки мы передадим переменные: NX_TAG=pr-123 npx nx build. Приведенная выше пользовательская конфигурация webpack позволит переменным окружения быть доступными в браузере в виде process.env, как в приложении node.

В esbuild у нас больше нет прямого способа получить эти переменные окружения. В результате, вместо того чтобы пытаться внедрить process.env в браузер, мы просто пишем JSON-файл во время сборки в процессе работы с Github Actions:

- name: Pass the build tag into the app without using process.env
  run: printf '{"tag":"%s"}\n' "${{ inputs.tag }}" > env.json
+  import env from './env.json';
   export default {
     APP_ENV: getEnv(),
-    APP_VERSION: process.env.NX_TAG ?? 'dev',
+    APP_VERSION: env.tag,
   };

Относительные импорты SCSS

Многие SCSS @import перестали работать:

✘ [ERROR] Can't find stylesheet to import.
  ╷
1 │ @import 'libs/heartbeat/src/lib/assets/scss/hb-foundations';
  │         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ╵

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

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

+ @import '../../../../heartbeat/src/lib/assets/scss/hb-foundations';
- @import 'libs/heartbeat/src/lib/assets/scss/hb-foundations';

Другое решение - добавить корень хранилища в качестве пути, который может быть использован для импорта SCSS, используя project.json:

      "build": {
        "executor": "@angular-devkit/build-angular:browser-esbuild",
        "options": {        
          "stylePreprocessorOptions": {
            "includePaths": [
               "node_modules",
+              ""
            ]
          }

Эталон производительности

Теперь, когда всё готово, давайте проверим, как это повлияло на производительность сборки:

Сборка CI на Github Actions с помощью стандартного запуска Ubuntu
Сборка CI на Github Actions с помощью стандартного запуска Ubuntu

Наша сборка занимает довольно много времени на Github actions, сэкономить 47 секунд здесь приятно, но это мало что изменит. Большую часть времени в нашем CI занимают тесты E2E.

Локальная сборка на Macbook M2
Локальная сборка на Macbook M2

Здесь мы начинаем видеть действительно хорошие результаты. Ждать 1 минуту перед компьютером, пока завершится nx build, очень мучительно.

Большинство людей на самом деле используют не build, а serve, так что давайте посмотрим, как улучшится dev-сервер.

Локальный сервер разработки
Локальный сервер разработки

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

Прирост здесь действительно заметен. Экономия более 20 секунд на времени запуска nx serve станет приятным улучшением качества жизни для всех разработчиков проекта.

Перестройка после изменения кода
Перестройка после изменения кода

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

Здесь ничего не меняется с помощью Vite:

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

Заключение

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

Если у вас есть приложение Angular, но вы еще не перешли на esbuild, вы упускаете возможность. Здорово видеть, что в Angular появляются такие улучшения!

Однако нам все еще чего-то не хватает, если сравнивать с тем, что предлагает Vite: на React+Vite dev-сервере изменение кода не вызывает полной перезагрузки страницы и мгновенно отражается в браузере. В Angular по-прежнему нужно перезагружать всю страницу.

Надеемся, что в будущем мы увидим в Angular+Vite новые дополнения, такие как Hot Module Replacement (HMR)!

Благодарю за прочтение! Счастливого кодинга!

Чтобы перейти к источнику, нажмите здесь. Требуется VPN.

#JavaScript #Angular #TypeScript
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу