Code splitting компонентов

Code splitting — это метод оптимизации загрузки приложений, при котором большие блоки кода разделяются на более мелкие части (чанки), загружаемые по мере необходимости. В контексте SvelteKit это особенно важно для ускорения начальной загрузки страницы и снижения времени до интерактивности.

SvelteKit по умолчанию поддерживает динамический импорт модулей. Любой компонент можно загрузить асинхронно с использованием синтаксиса import(). При этом Webpack или Vite создают отдельный JS-файл для этого компонента, который будет загружен только тогда, когда он реально требуется.

<script>
  let AsyncComponent;
  
  async function loadComponent() {
    const module = await import('$lib/components/LargeComponent.svelte');
    AsyncComponent = module.default;
  }
</script>

<button on:click={loadComponent}>Загрузить компонент</button>
{#if AsyncComponent}
  <svelte:component this={AsyncComponent} />
{/if}

В этом примере компонент LargeComponent не включается в основной бандл, а загружается динамически при клике на кнопку. Это снижает размер исходного JavaScript и ускоряет рендеринг страницы.


Использование svelte:component для динамического рендеринга

Ключевой инструмент для динамического подгружаемых компонентов в SvelteKit — это тег <svelte:component>. Он позволяет рендерить любой компонент, переданный через переменную, что идеально сочетается с code splitting:

  • Гибкость выбора компонента на основе состояния приложения или пользовательских действий.
  • Отложенная загрузка больших библиотек или визуальных элементов.
  • Уменьшение первоначального бандла, что критично для мобильных устройств с медленным интернетом.
<script>
  let ComponentToRender;

  async function showModal(type) {
    const module = await import(`$lib/modals/${type}.svelte`);
    ComponentToRender = module.default;
  }
</script>

<button on:click={() => showModal('LoginModal')}>Вход</button>
{#if ComponentToRender}
  <svelte:component this={ComponentToRender} />
{/if}

Оптимизация маршрутов с помощью +page.svelte и +layout.svelte

SvelteKit использует файловую структуру для маршрутов, что позволяет применять route-based code splitting. Каждый файл +page.svelte и +layout.svelte автоматически создается в отдельный бандл:

  • +layout.svelte делит код, общий для всех дочерних страниц.
  • +page.svelte загружается только при переходе на соответствующую страницу.

Пример структуры:

src/routes/
├─ index/+page.svelte      // основной бандл
├─ dashboard/+page.svelte  // загружается отдельно
├─ dashboard/+layout.svelte // общий код для dashboard

Таким образом, пользователь, заходящий на главную страницу, не загружает код панели управления, пока не перейдет на /dashboard.


Комбинация динамического импорта и UI библиотек

Большие UI-библиотеки, такие как svelte-material-ui или shadcn-ui, часто содержат множество компонентов. Чтобы избежать загрузки всей библиотеки сразу, рекомендуется:

  1. Импортировать только используемые компоненты через динамический импорт.
  2. Использовать <svelte:component> для рендеринга после загрузки.
  3. При необходимости оборачивать динамические импорты в обертку с Suspense-подобной функциональностью (через собственный спиннер или skeleton-компонент).

Пример:

<script>
  import Spinner from '$lib/ui/Spinner.svelte';
  let ButtonComponent;
  let loading = false;

  async function loadButton() {
    loading = true;
    const module = await import('svelte-material-ui/button');
    ButtonComponent = module.default;
    loading = false;
  }
</script>

{#if loading}
  <Spinner />
{:else if ButtonComponent}
  <svelte:component this={ButtonComponent}>Нажми меня</svelte:component>
{/if}

<button on:click={loadButton}>Загрузить кнопку</button>

Ленивая загрузка данных и компонентов

Code splitting можно сочетать с отложенной загрузкой данных. В SvelteKit используется load функция, которая возвращает промис, и компоненты можно подгружать параллельно с запросами к серверу, не блокируя рендеринг страницы:

<script context="module">
  export async function load({ fetch }) {
    const dataPromise = fetch('/api/items').then(res => res.json());
    const componentPromise = import('$lib/components/ItemList.svelte');
    const [data, module] = await Promise.all([dataPromise, componentPromise]);
    return { props: { data, ItemList: module.default } };
  }
</script>

<script>
  export let data;
  export let ItemList;
</script>

<svelte:component this={ItemList} {data} />

Такой подход минимизирует время ожидания и позволяет максимально эффективно использовать ресурсы браузера.


Рекомендации по структуре и поддержке

  • Разделять крупные компоненты на мелкие: каждый модуль легче загрузить динамически.
  • Избегать лишних зависимостей в главном бандле: импортировать их только по необходимости.
  • Использовать vite оптимизации: SvelteKit с Vite поддерживает prefetching и preloading чанков через <a sveltekit:prefetch>.

Prefetching пример:

<a sveltekit:prefetch href="/dashboard">Перейти в Dashboard</a>

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


Code splitting в SvelteKit сочетает в себе динамический импорт, <svelte:component>, файловую структуру маршрутов и ленивую загрузку данных. Такой подход позволяет строить быстрые, отзывчивые приложения, минимизируя первоначальный размер JavaScript и ускоряя интерактивность интерфейса.