Модули и инструменты — Примеры
Пример 1 — Один файл, глобальные имена и работа, выполняемая при импорте
Всё, что нужно приложению, живёт в одном скрипте, а вызов аналитики выполняется как побочный эффект загрузки модуля. Скажите, чего не хватает границе файла, и разделите код так, чтобы каждый модуль заявлял, что он предлагает, и ничто не выполнялось случайно.
// utils.js — каждое имя глобально
function formatMoney(cents) { /* ... */ }
function buildInvoice(order) { /* ... */ }
function sendReceipt(invoice) { /* ... */ }
// entry.js
import "./analytics.js"; // голый импорт; эффект невидим
trackPageView(); // и это тоже выполняется на верхнем уровне модуля
Решение
// format.js
export const CURRENCY = "GBP";
export function formatMoney(cents) { /* ... */ }
// invoice.js
import { CURRENCY, formatMoney } from "./format.js";
export function buildInvoice(order) { /* ... */ }
// entry.js — читается как список импортов и один осознанный вызов
import { buildInvoice } from "./invoice.js";
import { trackPageView } from "./analytics.js";
trackPageView();
Именованные экспорты дают каждому файлу границу: invoice.js точно заявляет, что ему нужно из format.js, опечатка в имени падает сразу, а граф зависимостей становится чем-то, что сборщик умеет прочитать. Модуль выполняется один раз, в порядке графа, поэтому вызов верхнего уровня происходит в момент, которого никто не выбирал, и его нельзя ни повторить, ни отменить; однако если экспортировать функцию и вызвать её из точки входа, время выбирает автор.
Пример 2 — Тяжёлый редактор в начальном бандле
Редактор форматированного текста импортируется в начале модуля, поэтому каждый посетитель скачивает его до того, как страница станет интерактивной. Открывают его при этом немногие. Скажите, что сборщик вынужден сделать, и загружайте редактор тогда, когда он понадобится.
import { Editor } from "./editor.js"; // ~240 kB, всегда в бандле точки входа
export function openEditor() {
return new Editor();
}
Решение
export async function openEditor() {
const { Editor } = await import("./editor.js"); // отдельный чанк
return new Editor();
}
// вызывающий код
button.addEventListener("click", async () => openEditor());
Статический import входит в граф, который сборщик обязан включить в бандл точки входа, поэтому 240 kB редактора оплачивает каждый, кто загружает страницу. import() возвращает промис и разрешается во время выполнения — благодаря этому сборщик выделяет отдельный чанк, а запрашивается он тем взаимодействием, которому нужен. Плата — короткое ожидание при первом использовании функции и async-функция, которую вызывающему придётся обработать.