‹Link rel =” prefetch / preload ”› в webpack

webpack 4.6.0 добавляет поддержку предварительной выборки (и предварительной загрузки).

TL; DR: используйте import(/* webpackPrefetch: true */ "...") для предварительной выборки.

Что такое ‹link rel =" prefetch "›?

Эта «Подсказка о ресурсах» сообщает браузеру, что это ресурс, который вероятно понадобится для некоторой навигации в будущем.

Браузеры обычно получают этот ресурс, когда они находятся в состоянии ожидания. После получения ресурс остается готовым в HTTP-кеше для выполнения будущих запросов. Множественные подсказки предварительной выборки ставятся в очередь и извлекаются во время простоя. При выходе из состояния ожидания во время предварительной выборки браузер может отменить любую текущую выборку (и поместить частичный ответ в кеш, чтобы продолжить с заголовками Content-Range) и прекратить обработку очереди предварительной выборки.

Подводя итог: получение при простое.

Что такое ‹link rel =” preload ”›?

Эта «Подсказка о ресурсах» сообщает браузеру, что это ресурс, который определенно необходим для этой навигации, но будет обнаружен позже. Chrome даже выводит предупреждение, если ресурс не используется через 3 секунды после загрузки.

Браузеры обычно получают этот ресурс со средним приоритетом (без блокировки макета).

Подводя итог: получайте как обычно, только что обнаруженное ранее.

Почему это полезно?

Предварительная загрузка используется для более раннего обнаружения ресурсов и предотвращения водопадной загрузки. Это может снизить загрузку страницы до двух циклов приема-передачи (1. HTML, 2. все остальные ресурсы). Его использование не требует дополнительных затрат на пропускную способность.

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

Разделение кода

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

В качестве примера мы возьмем домашнюю страницу, которая содержит LoginButton, который открывает LoginModal. После входа в приложение приложение переводит пользователя на DashboardPage. Страницы могут содержать другие кнопки, но они встречаются реже.

Чтобы добиться максимальной производительности, вы использовали import("LoginModal") в LoginButton, чтобы минимизировать домашнюю страницу. Аналогично LoginModal содержит import("DashboardPage").

Теперь этот пример приложения разделен как минимум на 3 блока: домашний блок, блок входа в систему, блок панели управления. При начальной загрузке необходимо загрузить только домашний блок, что создает отличный UX. Но когда пользователь нажимает кнопку LoginButton, происходит задержка до открытия LoginModal, потому что сначала необходимо загрузить эту часть приложения. Аналогично DashboardPage.

Использование предварительной загрузки в веб-пакете

Новая функция предварительной выборки позволяет улучшить этот рабочий процесс. И это очень просто.

В кнопке LoginButton измените
import("LoginModal") на
import(/* webpackPrefetch: true */ "LoginModal").

Подобно этому изменению с
import("DashboardPage") на
import(/* webpackPrefetch: true */ "DashboardPage") в LoginModal.

Это даст указание webpack выполнить предварительную выборку (во время простоя браузера) этого блока, загруженного по требованию, когда родительский блок завершит загрузку. В этом примере: когда загрузка домашнего фрагмента завершится, добавьте фрагмент входа в очередь предварительной выборки. Когда загрузка блока входа в систему завершится (реальная загрузка, а не предварительная выборка), добавьте блок панели мониторинга в очередь предварительной выборки.

Предполагая, что домашний блок является точкой входа, это сгенерирует <link rel="prefetch" href="login-chunk.js"> на HTML-странице.
Блок входа в систему является блоком по требованию, поэтому среда выполнения веб-пакета позаботится о внедрении <link rel="prefetch" href="dashboard-chunk.js"> после загрузки блока входа в систему. завершенный.

Это улучшит UX следующим образом:

Пользователь посещает домашнюю страницу. UX и производительность остались на прежнем уровне. После завершения загрузки HomePage браузер переходит в состояние ожидания и в фоновом режиме начинает получать фрагмент входа в систему. Пользователь этого не замечает. Предполагая, что пользователю нужно некоторое время, чтобы найти LoginButton, этот запрос завершится до того, как пользователь нажмет кнопку.
Когда пользователь теперь нажимает кнопку, фрагмент входа уже находится в кеше HTTP, и запрос попадает в кеш, и только занимает минимальное количество времени. Пользователь мгновенно увидит LoginModal.
В то же время среда выполнения webpack добавляет блок панели мониторинга в очередь предварительной выборки, поэтому не потребуется дополнительное время для загрузки блока после входа в систему.

Обратите внимание, что у пользователя не всегда может быть этот мгновенный интерфейс LoginModal. Существует множество факторов, которые могут повторно добавить задержку загрузки блока в LoginButton: медленные сети, быстрые щелчки пользователей, отключенная предварительная выборка на устройствах с ограниченной пропускной способностью, отсутствие поддержки предварительной выборки в браузере, очень медленное выполнение блока,…

Использование предварительной загрузки в webpack

Подобно import(/* webpackPrefetch: true */ "..."), можно использовать
import(/* webpackPreload: true */ "..."). У этого есть множество отличий от предварительной выборки:

  • Предварительно загруженный фрагмент начинает загружаться параллельно родительскому фрагменту. Предварительно выбранный фрагмент начинается после завершения родительского фрагмента.
  • Предварительно загруженный фрагмент имеет средний приоритет и загружается мгновенно. Предварительно выбранный фрагмент загружается во время простоя браузера.
  • Предварительно загруженный фрагмент должен быть немедленно запрошен родительским фрагментом. Предварительно выбранный фрагмент можно использовать в любое время в будущем.
  • Поддержка браузеров другая.

Из-за этого свойства варианты использования редки. Его можно использовать, если модуль всегда import() что-то мгновенно. Это может иметь смысл, если компонент зависит от большой библиотеки, которая должна находиться в отдельном фрагменте. Пример: ChartComponent использует большую ChartingLibrary. При использовании мгновенно отображается индикатор LoadingIndicator
import(/* webpackPreload: true */ "ChartingLibrary"). Когда запрашивается страница, которая использует ChartComponent, блок-библиотека диаграмм также запрашивается через <link rel="preload">. Предполагая, что фрагмент страницы меньше и завершается быстрее, страница будет отображаться с индикатором LoadingIndicator до тех пор, пока не завершится уже запрошенный фрагмент библиотеки диаграмм. Это немного увеличит время загрузки, так как для этого потребуется только один цикл приема-передачи вместо двух. Особенно в средах с высокой задержкой.

Неправильное использование webpackPreload может снизить производительность, поэтому будьте осторожны при его использовании.

Размышляя о предварительной загрузке и предварительной выборке, вы, вероятно, захотите использовать предварительную выборку.
Примечание. Это утверждение относится к webpack import(). В HTML-страницах вам, вероятно, понадобится предварительная загрузка.

Несколько предварительно загруженных фрагментов

Вы можете добавить флаг webpackPrefetch к сколь угодно большому числу import(), но учтите, что все предварительно выбранные блоки борются за пропускную способность. Они фактически поставлены в очередь, и действительно используемый фрагмент может не быть предварительно выбран, когда пользователь его запрашивает.

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

Тебе повезло, мы тебя прикрыли. (Обратите внимание, что это расширенный вариант использования.)

Вместо использования webpackPrefetch: true вы можете передать число в качестве значения. webpack выполнит предварительную загрузку фрагментов в указанном вами порядке. Пример: webpackPrefetch: 42 будет предварительно выбран перед webpackPrefetch: 1, который будет предварительно выбран перед webpackPrefetch: true, который будет предварительно выбран перед webpackPrefetch: -99999 (как z-порядок). На самом деле true считается 0.

Аналогично webpackPreload, но я не думаю, что вам это понадобится!

часто задаваемые вопросы

Что если несколько import()s запрашивают один и тот же фрагмент, а некоторые из них предварительно загружены / загружены?
Предварительная загрузка имеет преимущество перед предварительной выборкой, которая не имеет никакого значения. В той же категории побеждает высший орден.

Что делать, если предварительная выборка / предварительная загрузка не поддерживается в браузере?
Браузер игнорирует их. webpack не пытается сделать откат. В любом случае это подсказка, поэтому даже браузер, который ее поддерживает, может проигнорировать ее, если сочтет это так.

У меня не работает предварительная загрузка / предварительная загрузка в точке входа. В чем проблема?
Среда выполнения веб-пакета заботится только о предварительной выборке / предварительной загрузке для фрагментов, загружаемых по запросу. Когда предварительная выборка / предварительная загрузка используется в import()s в блоке записи, генерация html отвечает за добавление тегов <link> в HTML. Данные статистики json предоставляют информацию в entrypoints[].childAssets.

Почему бы не использовать предварительную выборку / предварительную загрузку на каждом import()?
Вы тратите много трафика. Также более выгодно использовать его выборочно для import()s, которые с большой вероятностью будут посещены. Не теряйте пропускную способность. У некоторых людей ограниченный объем или пропускная способность. Используйте выборочно, чтобы получить максимальную пользу!

Мне не нужна / не нужна эта функция. Какова плата за неиспользование?
Код времени выполнения добавляется только тогда, когда эта функция используется в одном из ваших блоков, загружаемых по запросу. Поэтому, когда вы не используете его, вам не нужно платить накладные расходы.

У меня уже есть волшебный комментарий на import(). Могу ли я добавить несколько волшебных комментариев?
Да, через , или в отдельные комментарии:

import(
  /* webpackChunkName: "test", webpackPrefetch: true */
  "LoginModal"
)
// or
import(
  /* webpackChunkName: "test" */
  /* webpackPrefetch: true */
  "LoginModal"
)
// spacing optional

Я создаю свою библиотеку с помощью накопительного пакета, а также использую import(). Могут ли пользователи моей библиотеки, которые используют веб-пакет, получить выгоду от предварительной выборки?
Да, вы можете добавить волшебные комментарии веб-пакета в import(), и при накоплении этот комментарий сохранится. Когда результат создается с помощью webpack, он будет использовать комментарий. Если не собран с помощью webpack, комментарий удаляется минимизаторами.

Я не уверен, что добавление упреждающей выборки для определенного import() улучшает производительность. Стоит ли добавить?
Лучше всего измерить. A / B тестирование. Здесь нет общего совета. Это зависит от вероятности посещения пользователями этого пути приложения.

У меня уже есть сервис-воркер, который кэширует все приложение при загрузке. Имеет ли мне смысл использовать предварительную выборку?
Это зависит от вашего приложения. В общем, этот вид Service Worker загружает все активы приложения без частичного порядка, в то время как предварительная выборка позволяет указать порядок и зависит от фактического положения пользователя в приложении. Предварительная выборка также более эффективна с точки зрения пропускной способности и загружается только во время простоя браузера.
Измерьте, сколько времени занимает загрузка всего приложения в средах с низкой пропускной способностью по сравнению с тем, когда происходит первая навигация пользователя. В огромных приложениях вместо этого имеет смысл использовать предварительную выборку.

Я хочу разделить мою начальную загрузку страницы на критические ресурсы и некритические ресурсы и загрузить их в этом порядке. Помогает ли здесь предварительная загрузка?
Да, вы можете поместить критические ресурсы в точку входа, а некритические ресурсы за import() с помощью webpackPreload: true. Обратите внимание: не забудьте также добавить <link rel="preload"> для ресурсов блока записи (перед тегами для дочерних элементов).

webpack не поддерживается крупной компанией, в отличие от многих других крупных продуктов с открытым исходным кодом. Разработка финансируется за счет пожертвований. Если вы зависите от webpack, подумайте о пожертвовании… (Спросите своего босса!)

Особая благодарность этим спонсорам: (Топ 5)

  • Trivago (метапоиск отелей) пожертвовал 100 000 долларов.
  • Компания ag-Grid (DataGrid) пожертвовала 35 000 долларов США.
  • Сегмент (Инфраструктура данных) пожертвовал в общей сложности 24 000 долларов США.
  • Adobe (Программное обеспечение) пожертвовала в общей сложности 12 000 долларов США.
  • Capital One (Банк) пожертвовал $ 12 000
  • "Полный список"