AngularJS + Webpack = lazyLoad

Вступление

Одной из распространенных проблем, с которыми сталкивается большинство разработчиков при написании одностраничного приложения, является создание и загрузка модулей lazyLoad в ответ на определенное действие, выполняемое пользователем, или нажатие на URL-адрес (в большинстве случаев), который загружает набор зависимостей, таких как JavaScript, CSS, HTML и т. Д. В реалиях современной Front-End разработки это был бы огромный файл JavaScript. Что ж, в этой статье я хотел бы поделиться своим опытом и показать вам, как реализовать модули lazyLoad на AngularJS и уменьшить время запуска при первом запуске приложения.

Почему AngularJS 1.x

Вы можете спросить: Зачем работать с AngularJS 1.x, если доступен« Angular v5.2 ?» Вопрос более чем уместный. Однако не забывайте, что многие проекты по-прежнему используют AngularJS 1.x и чувствуют себя неплохо. Для них, как и для многих других отраслей, переход на новую версию обойдется дорого как по человеко-часам, так и по деньгам. AngularJS 1.x остается востребованным на рынке.

Велосипеды, которые уже существуют

Стало обычным делом, что в мире Front-End разработки существует множество инструментов / подходов, которые можно использовать для решения одной и той же проблемы, и каждый стремится выбрать тот, который лучше всего соответствует его потребностям, навыкам и знаниям. И это не обязательно плохо. Вы должны принять это и продолжать развиваться. Кто-то выбирает RequireJS, кто-то выбирает curl.js, кто-то выбирает Browserify, кто-то предпочитает [вставить свой любимый инструмент]. В этой статье я хочу показать вам, как реализовать загрузку модулей lazyLoad с помощью Webpack, UI-Router и ocLazyLoad. Вся закулисная магия будет выполняться с помощью инструмента ocLazyLoad. Если мы попытаемся создать модуль lazyLoad, используя, например, require.ensure без ocLazyLoad, мы получим ошибку такого типа:

Структура проекта

Поехали. Поскольку я не могу предоставить доступ к исходному коду проекта, для которого мне пришлось реализовать модули lazyLoad, я создал небольшое приложение. Я постарался сделать его как можно проще, чтобы он не выглядел как космический зонд, и вы могли ясно видеть, что из чего исходит. Основная задача заключалась в создании рабочего прототипа, который был бы доступен онлайн (не только источники). Немного ниже вы найдете структуру проекта для веток require-ensure и system-import. Для ветки import-es6 внесем поправки.

Теперь, прежде чем мы перейдем к коду, я хотел бы подчеркнуть два важных момента, которые напрямую влияют на то, получим ли мы модуль lazyLoad или нет:

  1. Чтобы модуль стал lazyLoad, не следует определять его как зависимость по отношению к другим модулям.
  2. Модуль нельзя импортировать никуда, кроме маршрута, для которого вы хотите создать этот модуль lazyLoad.

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

require.ensure () + $ ocLazyLoad

require.ensure был введен командой webpack в первой из его версий. Этот метод позволяет разработчикам динамически создавать отдельные файлы (называемые фрагментами в терминах веб-пакетов), содержащие определенные фрагменты кода, которые в дальнейшем будут загружены пользователю в ответ на определенное действие. И хотя указанный метод по своей сути не лучший из доступных для выполнения этой задачи, если вы все же предпочитаете его, в этом нет ничего плохого. Этот метод отлично подойдет тем, кто хочет создавать модули lazyLoad, не тратя слишком много на рефакторинг. Ниже вы можете найти пример применения require.ensure для загрузки index.module.js:

Для about.module.js код будет идентичным, с той лишь разницей, что пути к модулю и некоторые другие параметры будут немного отличаться. Ниже вы можете увидеть пример использования require.ensure для загрузки about.module.js:

Как вы могли заметить, вся магия происходит в этой строке:
$ocLazyLoad.load(module.HOME_ABOUT_MODULE);

Есть еще один способ определения модуля - использование объекта:
$ocLazyLoad.load({ name: "home.module" });

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

Обращаю внимание на один очень важный нюанс, касающийся about.module.js и дальнейшей загрузки приложений пользователю. Взгляните на скриншот ниже:

При нажатии на ссылку Home/About одновременно загружаются два файла: index.module.chunk.js и about.module.chunk.js. Это происходит потому, что home.about URL является дочерним по отношению к home URL. Вы должны иметь это в виду. Забегая немного вперед, в последнем разделе мы добавим еще один модуль с новым URL-адресом и увидим, что он загрузит только один файл и ничего больше.

System.import + $ ocLazyLoad

Я долго думал, писать об этом подходе или нет, а потом решил в пользу этого. System.import - это еще одна производная от команды webpack, которая, несмотря на запрет, по-прежнему остается одним из предлагаемых вариантов реализации. Более того, эта конструкция продолжает работать в новых версиях webpack. Я подозреваю, что это так по причинам совместимости. Если вы используете эту конструкцию в своем проекте, у меня для вас плохие новости - в настоящее время она находится в статусе устаревшая. Хорошо, пойдем дальше.

Динамический импорт + $ ocLazyLoad

Возможно, вы уже слышали, что Chrome 63 и Safari Technology Preview 24 выпустили несколько обновлений, и теперь разработчики могут получить доступ к динамическому импорту. Да, вы правы, тот очень динамичный импорт, который предлагался в спецификации. Еще в 2016 году команда webpack впервые применила поддержку динамического импорта. В этом разделе мы добавим еще один модуль в корень каталога pages, чтобы убедиться, что lazyLoad работает правильно. Структура ветки import-es6 представлена ​​ниже:

Если вы не используете в своем проекте ни Babel, ни TypeScript, то все будет работать сразу из коробки. Но вы не хуже меня знаете, что в реалиях современного Front End очень сложно написать код, не используя ни один из этих двух инструментов. Давайте посмотрим на Вавилон поближе. Сначала нам нужно установить дополнительный плагин для babel, который понимает синтаксис динамического импорта: syntax-dynamic-import. Если мы этого не сделаем, мы получим ошибку:

Затем нам нужно добавить .babelrc с заданными настройками:

А вот и вторая неприятная ошибка, которую вы можете увидеть ниже:

Да, верно, ESLint тоже не понимает динамический импорт. Чтобы исправить это, нам нужно установить специальный парсер для ESLint babel-eslint, и как только мы это сделаем, все будет работать без сбоев. Теперь добавим .eslintrc с настройками:

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

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

Компонент против шаблона

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

Полезные ссылки

  1. Как перейти к компонентам AngularJS 1.5
  2. Ленивая загрузка в UI-Router
  3. Исходный код на GitHub
  4. Проект на Heroku

Вместо заключения

Использование модулей lazyLoad в контексте приложений AngularJS - отличный способ сделать ваше приложение менее ресурсоемким, более отзывчивым и лучше распределенным. Времена меняются, требования к приложениям растут, как и объем кода, который мы доставляем пользователю. Если раньше было достаточно собрать весь код в один файл, отдать его пользователю, и он был готов и вычищен, то теперь это стало чем-то вроде роскоши. Сейчас существует тенденция разделять приложение в зависимости от URL-адреса и выделять общий код.

На этом пока все. Спасибо за внимание и особая благодарность тем, кто дочитал эту статью до конца.

P.S. Если у вас был аналогичный опыт реализации модулей lazyLoad для приложений AngularJS - поделитесь своими комментариями ниже.