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:





Заключение

Разделение кода - это мощный инструмент, ускоряющий работу вашего приложения, быстро загружая зависимости. Но как однажды сказал дядя Бен:

Чтобы использовать его с максимальной эффективностью, необходимо знать, как работает инструмент, и я надеюсь, что помог вам узнать о нем немного больше!

Ресурсы