Webpack и динамический импорт
В старых версиях Webpack (v1) мы обычно использовали AMD require или специальный Webpack require.ensure для модулей динамической загрузки. Но в этой статье я собираюсь использовать предложенный динамический импорт ES2015, поддерживаемый Webpack, начиная с версии 2, через плагин babel и дополнительные специфические функции Webpack для него.
Обновление: если вы используете Babel 7.5+, он уже включает плагин динамического импорта для вас;)
Динамический импорт: основы
Синтаксис довольно прост. В приведенном выше предложении ES ключевое слово import получает больше возможностей и также превращается в функцию , которая возвращает Обещание :
import("module/foo").then(foo => console.log(foo.default))
Приведенный выше код загрузит модуль foo во время выполнения и, разрешив его, зарегистрирует экспорт по умолчанию модуля . Поскольку import - это функция, получающая строку, мы можем делать мощные вещи, например загружать модули с помощью выражений.
Динамическая загрузка модуля с выражениями
Предположим, у вас есть приложение, которое отличается по поведению и визуальным эффектам от мобильных и настольных компьютеров. В этом случае наличие только адаптивного дизайна не соответствует вашим требованиям, поэтому вы создаете средство визуализации страницы, которое загружает и отображает страницу на основе платформы пользователя. Он в основном использует шаблон стратегии, который выбирает, какой модуль должен быть загружен во время выполнения. Как умный разработчик, вы не хотите загружать весь код для ПК, если пользователь работает с мобильных устройств, и наоборот. Давайте проверим это по приведенному ниже коду:
🤓 - «Но послушайте, это довольно упрощенный подход. В реальных приложениях вообще нет одной страницы! В моем приложении есть репозиторий компонентов с большим количеством страниц! »
Конечно, даже если мы динамически загружаем компоненты, это все равно довольно сложное решение. Давайте реорганизуем нашу функцию:
🤓- «Еще не хорошо! Мое приложение доступно на множестве конкретных платформ, таких как мобильные устройства, настольные ПК, планшеты, виртуальная реальность, и в будущем может стать еще более ! »
Хорошо-хорошо. Давайте снова проведем рефакторинг:
В этом примере мы используем более функциональный подход. Создана и экспортирована составная функция для выполнения работы, которая может загружаться для любой платформы, которую мы хотим, используя выражения, плюс мы уже предоставили два загрузчика, один для рабочего стола. и прочее для мобильных.
Проблемы при загрузке файлов
В Webpack мы обычно загружаем изображения как модули с помощью загрузчика файлов. Загрузчик файлов в основном отображает путь к полученному файлу внутри модуля.
import(`assets/images/${imageName}.jpg`).then( src => ... )
Проблема в том, что если вы хотите динамически загружать файл, в данном случае изображение, Webpack по умолчанию генерирует фрагмент для этого модуля, примерно так:
Большая проблема заключается в том, что когда вы запрашиваете динамически импортированные изображения, он выполняет сетевой запрос для получения фрагмента, а затем еще один для получения изображения, добавляя ненужные накладные расходы вашему приложению. Если вы используете HTTPS, то еще хуже!
🤓 - «Хорошо, я делаю это для большого количества изображений, это превратилось в большую проблему, и из-за дополнительных запросов изображения загружаются медленнее. Как решить эту проблему? »
Webpack «волшебные комментарии»
Webpack добавляет действительно приятную функцию динамическому импорту - волшебные комментарии. При этом вы можете добавить некоторые метаданные, читаемые для Webpack, таким образом, вы можете выбрать стратегию того, как Webpack генерирует и загружает блоки.
Режим Webpack
Чтобы решить проблему динамической загрузки файлов, мы можем просто выбрать стратегию загрузки:
import(/* webpackMode: "eager" */ `assets/images/${imageName}.jpg`)
Это заставит Webpack включить фрагмент файла в родительский пакет / фрагмент, заставив его не создавать для этого отдельный фрагмент. Таким образом, все пути к файлам будут быстро доступны, когда ваше приложение загрузит родительский пакет / блок.
Существует четыре разных метода (ленивый, один раз ленивый, нетерпеливый, слабый). Более подробно с описанием можно ознакомиться здесь.
Имя блока Webpack
🤓 - «Привет, я заметил, что Webpack просто помещает числа в сгенерированные блоки. Это усложняет отладку, так как я не знаю, был ли загружен конкретный фрагмент или нет! »
Поскольку мы можем контролировать стратегию загрузки, мы также можем использовать волшебные комментарии для управления сгенерированными именами блоков, просто сделав следующее:
import(/* webpackChunkName: "foo-image" */ "assets/images/foo.jpg"); import(/* webpackChunkName: "bar-module" */ "modules/bar");
Вместо чисел Webpack будет использовать выбранные имена для сгенерированных блоков.
Предварительная выборка / предварительная загрузка
Примечание: эта функция была добавлена в Webpack v4.6
Если вы используете HTTP2, лучше разбивать большие пакеты на более мелкие. Итак, лучше ли предварительно загрузить эти небольшие фрагменты изображения, чем добавлять их в более крупный пакет / фрагмент, верно?
Для этого мы можем просто использовать вместо webpackMode: «eager» webpackPrefetch: true, который делает браузер загружает фрагменты после родительского пакета / фрагмента.
🤓 - «Но в чем разница между предварительной выборкой и предварительной загрузкой?»
Документы Webpack объясняют более подробно:
- Предварительно загруженный фрагмент начинает загружаться параллельно родительскому фрагменту. Предварительно выбранный фрагмент начинается после завершения родительского фрагмента.
- Предварительно загруженный фрагмент имеет средний приоритет и загружается мгновенно. Предварительно выбранный фрагмент загружается во время простоя браузера.
- Предварительно загруженный блок должен быть немедленно запрошен родительским блоком. Предварительно выбранный фрагмент можно использовать в любое время в будущем.
- Браузерная поддержка отличается.
Поскольку предварительная выборка заставляет блок загружаться во время простоя, вы можете добавить числа в качестве параметра, чтобы сообщить Webpack, каков приоритет каждого из них:
// 0 is same as true import(/* webpackPrefetch: 0 */ "assets/images/foo.jpg"); // this loads first as 1 > 0 (or true) import(/* webpackPrefetch: 1 */ "modules/bar"); // this one will be the last! import(/* webpackPrefetch: -100 */ "modules/slowpoke");
Модуль bar.js имеет более высокий приоритет для загрузки, поэтому он будет предварительно загружен перед foo.jpg и slowpoke.js будет последним (приоритет -100) .
Углубляясь в разделение кода
Если вы хотите узнать, как создать одностраничное приложение с отложенной загрузкой (SPA), используя обсуждаемый динамический импорт, вы можете ознакомиться с двумя моими предыдущими статьи на эту тему. Хотя в статьях в примерах используются React и React + Redux, вы можете применить ту же идею в любом фреймворке / библиотеке на основе SPA:
Заключение
Разделение кода - это мощный инструмент, ускоряющий работу вашего приложения, быстро загружая зависимости. Но как однажды сказал дядя Бен:
Чтобы использовать его с максимальной эффективностью, необходимо знать, как работает инструмент, и я надеюсь, что помог вам узнать о нем немного больше!
Ресурсы