Back

Как лениво загружать компоненты в Svelte

Как лениво загружать компоненты в Svelte

Ваше приложение на Svelte может быть быстрым по умолчанию, но если вы включаете в начальную JavaScript-сборку редактор форматированного текста, библиотеку графиков или сложный виджет панели управления, пользователи загружают код, который они могут никогда не использовать. Ленивая загрузка на уровне компонентов в Svelte решает эту проблему, откладывая эти тяжёлые импорты до момента, когда они действительно понадобятся.

В отличие от React, в Svelte нет встроенного хелпера lazy(). Вместо этого ленивая загрузка опирается непосредственно на нативную JavaScript-функцию import() в сочетании с условным рендерингом. Это более ручной подход, но он также более гибкий.

Ключевые выводы

  • В Svelte отсутствует встроенный хелпер lazy(), поэтому ленивая загрузка использует нативную JavaScript-функцию import() с условным рендерингом
  • SvelteKit автоматически обрабатывает разделение кода на уровне маршрутов, но ленивая загрузка на уровне компонентов всё ещё необходима для тяжёлых виджетов внутри одной страницы
  • Vite разделяет динамические импорты на отдельные чанки без какой-либо конфигурации
  • Три основных паттерна охватывают большинство сценариев использования: загрузка по требованию с {#await}, предзагрузка при наведении и отложенная загрузка, когда браузер простаивает

Почему ленивая загрузка на уровне компонентов всё ещё важна в SvelteKit

SvelteKit уже автоматически обрабатывает разделение кода на уровне маршрутов. Каждый маршрут получает свой собственный чанк, поэтому навигация между страницами не раздувает начальную сборку. Но внутри одной страницы всё, что вы импортируете в начале .svelte-файла, объединяется вместе и загружается сразу.

Вот где ленивая загрузка на уровне компонентов становится ценной. Если страница содержит тяжёлый компонент графика, видеоплеер или модальное окно, которое появляется только после взаимодействия пользователя, нет причин загружать этот код при загрузке страницы.

SvelteKit собирается с помощью Vite, который нативно обрабатывает динамические импорты. Когда Vite видит import('./HeavyChart.svelte'), он автоматически разделяет этот модуль на отдельный чанк. Никакой дополнительной конфигурации не требуется.

Как лениво загружать компоненты Svelte: основные паттерны

Базовая ленивая загрузка с {#await}

Самый простой подход использует блок {#await} Svelte непосредственно в шаблоне:

<!-- src/routes/+page.svelte -->
<script>
  let showChart = false;
  let chartData = [1, 2, 3];
</script>

<button onclick={() => showChart = true}>Load Chart</button>

{#if showChart}
  {#await import('$lib/components/HeavyChart.svelte')}
    <p>Loading chart...</p>
  {:then Chart}
    <Chart.default data={chartData} />
  {:catch error}
    <p>Failed to load chart: {error.message}</p>
  {/await}
{/if}

Это работает как в Svelte 4, так и в Svelte 5. Блок {:catch} важен — сетевые сбои случаются, а тихие ошибки усложняют отладку. В Svelte 4 динамическое переключение компонентов обычно использует <svelte:component this={Component}>. В современном Svelte изменение ссылки на компонент также может напрямую запускать повторный рендеринг.

Браузер кэширует модуль после первого импорта. Последующие рендеры того же компонента не вызывают новый сетевой запрос.

Загрузка при наведении для более быстрой воспринимаемой производительности

Распространённый паттерн — начинать загрузку, когда пользователь сигнализирует о намерении, наводя курсор на элемент-триггер:

<!-- src/routes/+page.svelte -->
<script lang="ts">
  import type { Component } from 'svelte';

  let HeavyWidget: Component<{ message: string }> | null = $state(null);

  async function loadWidget() {
    if (!HeavyWidget) {
      const module = await import('$lib/components/HeavyWidget.svelte');
      HeavyWidget = module.default;
    }
  }
</script>

<div onmouseenter={loadWidget}>
  <p>Hover to preload the widget</p>
</div>

{#if HeavyWidget}
  <HeavyWidget message="Ready!" />
{/if}

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

Загрузка, когда браузер простаивает

Для некритичных компонентов, которые улучшают страницу, но не нужны немедленно, используйте requestIdleCallback:

<script lang="ts">
  import { onMount } from 'svelte';
  import type { Component } from 'svelte';

  let FeedbackWidget: Component | null = $state(null);

  onMount(() => {
    const load = () =>
      import('$lib/components/FeedbackWidget.svelte').then(
        (m) => (FeedbackWidget = m.default)
      );

    if ('requestIdleCallback' in window) {
      requestIdleCallback(load);
    } else {
      setTimeout(load, 300); // fallback для Safari
    }
  });
</script>

{#if FeedbackWidget}
  <FeedbackWidget />
{/if}

Обратите внимание, что поддержка requestIdleCallback в Safari исторически была непоследовательной, поэтому рекомендуется использовать fallback с setTimeout.

Когда не стоит использовать ленивую загрузку

Не каждый компонент выигрывает от ленивой загрузки. Избегайте её для:

  • UI в верхней части экрана — навигация, главные секции, основной контент
  • Маленьких компонентов — накладные расходы на асинхронность перевешивают экономию
  • Компонентов, необходимых сразу при монтировании — мигание загрузки ухудшает UX

Чрезмерное разделение создаёт множество маленьких асинхронных чанков. Современные сборщики, такие как Vite, эффективно с этим справляются, но всё же существует точка убывающей отдачи.

Заключение

Ленивая загрузка компонентов Svelte сводится к одному: использованию import() вместо статического импорта с последующим условным рендерингом результата. Сборка SvelteKit на основе Vite автоматически обрабатывает разделение кода. Три паттерна выше — по требованию, при наведении и в режиме простоя — охватывают большинство реальных сценариев. Выберите триггер, который соответствует моменту, когда пользователям действительно нужен компонент, добавьте обработку ошибок, и ваша начальная сборка останется компактной.

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

Динамический импорт работает в обеих версиях. В Svelte 4 вы обычно рендерите загруженный компонент, используя svelte:component с атрибутом this. В Svelte 5 вы можете использовать разрешённый компонент напрямую как тег в шаблоне. Базовый механизм импорта одинаков в обоих случаях.

Динамические импорты выполняются и во время SSR, поскольку Node.js их поддерживает. Однако ленивая загрузка — это в первую очередь клиентская оптимизация, направленная на уменьшение начальной JavaScript-нагрузки браузера. Если компонент должен рендериться на сервере для SEO или первой отрисовки, статический импорт — лучший выбор.

Проверьте вывод сборки или используйте анализатор бандла для выявления больших чанков. Компоненты, которые подключают тяжёлые сторонние библиотеки, такие как рендереры графиков, редакторы форматированного текста или виджеты карт, являются сильными кандидатами. Если компонент добавляет менее нескольких килобайт, накладные расходы на дополнительный сетевой запрос, вероятно, перевешивают экономию.

Нет. Браузер кэширует модуль после разрешения первого динамического импорта. При последующих рендерах закэшированный модуль возвращается немедленно, поэтому состояние загрузки появляется только при первой загрузке. Вы также можете предзагрузить модуль при наведении или во время простоя, чтобы полностью устранить видимую задержку.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay