В наши дни веб-приложения могут стать довольно большими. Панель инструментов, состоящая из 10 компонентов, еще одна страница с 2–6 компонентами и, конечно же, несколько сервисов, предоставляющих данные для всех отдельных компонентов.

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

Angular и Webpack уже предлагают несколько отличных готовых функций, которые помогут вам минимизировать размер ваших пакетов, таких как Минификация кода, Встряхивание дерева и Компилятор AOT (который автоматически активируется в режиме разработки). при запуске Angular 9 или выше, чтобы улучшить опыт разработчиков).

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

Ленивая загрузка модулей

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

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

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

Как видите, один большой файл может быть довольно ограниченным. Но что, если бы вы могли обслуживать множество небольших файлов? Это как раз и есть точка ленивой загрузки. Angular объединит ваши модули в небольшие файлы JavaScript в зависимости от страницы, на которой они используются. При запросе набора небольших файлов браузер может использовать несколько потоков для параллельной загрузки файлов. Таким образом, он может загружать файлы быстрее, что сокращает время загрузки вашего приложения.

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

Создание нашего демонстрационного приложения

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

Во-первых, нам нужно создать новый проект:

ng new question-answer-demo —-minimal --routing —-style=scss

Теперь, когда мы создали наш проект, мы можем начать программировать.

Добавление ленивой загрузки

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

ng generate module answer
ng generate component answer

Теперь, когда мы создали нашу страницу ответов, мы можем добавить ее в наш файл маршрутизации. Откройте src/app/app-routing.module.ts и замените маршруты на следующие:

Это загрузит нашу страницу ответов при запуске. .then(m => m.AnswerModule) загрузит фактический модуль, подключенный к странице, при запуске маршрута. Однако это загрузит только модуль, а не компоненты, связанные с этим модулем. Из-за этого нам нужно добавить путь маршрутизатора к определению модуля, который указывает маршрутизатору также загружать компонент при загрузке модуля.

Добавим этот путь в модуль, переходим в src/app/answer/answer.module.ts и добавляем следующий код:

Теперь, когда мы определили маршрут, нам нужно зарегистрировать его как дочерний маршрут с маршрутизатором, чтобы маршрутизатор знал, какой компонент загружать при загрузке модуля. Мы можем добиться этого, вызвав метод RouterModule.forChild.

Вот и все! Если мы теперь запустим наше приложение через ng serve, вы увидите, что страница ответов была загружена с текстом answer works!.
(Необязательно) Вы можете заменить параметр template декоратора app.component.ts следующим, чтобы было понятнее:

Просмотр результата

Итак, теперь у нас есть работающее приложение, загружается страница ответов, и все выглядит нормально. Но как мы можем проверить, действительно ли ленивая загрузка влияет на результат сборки? Просто запустите ng build и посмотрите на папку /dist. Там вы найдете отдельный файл модуля для модуля ответа:

Когда мы сравниваем два изображения выше, мы видим дополнительный файл во втором изображении с именем 414.9be(...). Если мы откроем этот файл, мы увидим несколько ссылок на наш компонент ответа. Это означает, что маршрутизатор Angular сообщил компилятору Angular, что файл может быть загружен позже других файлов (ленивая загрузка) и, таким образом, может быть отделен от основного пакета. Теперь у нас есть дополнительный файл, то есть отдельный файл, который может быть загружен браузером асинхронно с основным пакетом.

Миссия выполнена!

Предварительная загрузка всех модулей

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

Для этого у Angular Router есть второй трюк в рукаве. Мы можем изменить это поведение, установив для preloadingStrategy маршрутизатора Angular значение PreloadAllModules. Это позволяет маршрутизатору отложить загрузку пакетов, которые не требуются напрямую, на период бездействия после первоначальной загрузки приложения. При этом меньше ресурсов нужно загружать заранее, что приводит к сокращению времени загрузки вашего приложения.

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

Не забудьте добавить объект конфигурации вторым параметром метода .forRoot.

Вот и все! Это все, что нам нужно, чтобы заставить его работать. Если мы теперь запустим ng serve и посмотрим на вкладку сети в нашем браузере, мы увидим, что пакет src_app_answer_answer_module_ts.js загружается после первоначальной загрузки!

Заключение

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

Кроме того, с новым компилятором Ivy, который автоматически поставляется с Angular, начиная с версии 9, вы даже можете лениво загружать отдельные компоненты для еще большего контроля и повышения производительности. Хорошая статья о ленивой загрузке компонентов находится здесь, на Medium.

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