Отложенная загрузка некритичных данных

Отложенная загрузка (lazy loading) данных в TanStack Router позволяет оптимизировать производительность приложения, загружая только те данные, которые необходимы для рендера текущего маршрута, и откладывая загрузку менее критичных ресурсов до момента, когда они действительно понадобятся. Такой подход особенно эффективен для крупных приложений с множеством маршрутов и компонент, которые требуют обращения к серверу или выполнению сложных вычислений.

Ключевые преимущества включают сокращение времени первичной загрузки, снижение нагрузки на сеть и улучшение отзывчивости интерфейса.

Определение маршрутов с асинхронными данными

В TanStack Router маршруты описываются объектами, где ключевым элементом является loader. Для реализации отложенной загрузки некритичных данных используется возможность возвращать из loader промис, который будет выполняться асинхронно.

import { createRouter, RouterProvider } from '@tanstack/router';

const router = createRouter({
  routes: [
    {
      path: '/',
      element: <HomePage />,
      loader: async () => {
        const criticalData = await fetch('/api/critical').then(res => res.json());
        return { criticalData };
      },
      children: [
        {
          path: 'extra',
          element: <ExtraData />,
          loader: async () => {
            const extraData = await fetch('/api/extra').then(res => res.json());
            return { extraData };
          },
        },
      ],
    },
  ],
});

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

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

TanStack Router предоставляет функцию defer, которая позволяет разделять загрузку критичных и некритичных данных внутри одного маршрута. Критичные данные передаются сразу, а некритичные оборачиваются в отложенный промис.

import { defer } from '@tanstack/router';

const router = createRouter({
  routes: [
    {
      path: '/profile',
      element: <ProfilePage />,
      loader: () => {
        const userDataPromise = fetch('/api/user').then(res => res.json());
        const recommendationsPromise = fetch('/api/recommendations').then(res => res.json());

        return defer({
          userData: userDataPromise,             // критичные данные
          recommendations: recommendationsPromise, // некритичные, загрузка будет deferred
        });
      },
    },
  ],
});

defer возвращает объект, где свойства могут быть как синхронными, так и промисами. TanStack Router обеспечивает корректное управление состоянием загрузки, позволяя отображать скелетон или индикатор загрузки для некритичных данных, пока они подтягиваются.

Обработка отложенных данных в компонентах

Для работы с отложенными данными в компонентах используется хук useLoaderData совместно с функцией useAsyncData для получения состояния загрузки промисов. Это позволяет отделить рендеринг критичных данных от некритичных.

import { useLoaderData, useAsyncData } from '@tanstack/router';

function ProfilePage() {
  const { userData, recommendations } = useLoaderData();
  const { data: recData, isLoading } = useAsyncData(recommendations);

  return (
    <div>
      <h1>{userData.name}</h1>
      <section>
        <h2>Рекомендации</h2>
        {isLoading ? (
          <p>Загрузка рекомендаций...</p>
        ) : (
          <ul>
            {recData.map(item => (
              <li key={item.id}>{item.title}</li>
            ))}
          </ul>
        )}
      </section>
    </div>
  );
}

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

Совмещение с React Suspense

TanStack Router поддерживает интеграцию с React Suspense, что обеспечивает декларативное управление состояниями загрузки. Отложенные данные можно оборачивать в <Suspense> для показа placeholder-контента.

import { Suspense } from 'react';
import { useLoaderData, useAsyncData } from '@tanstack/router';

function ProfilePage() {
  const { userData, recommendations } = useLoaderData();

  return (
    <div>
      <h1>{userData.name}</h1>
      <Suspense fallback={<p>Загрузка рекомендаций...</p>}>
        <Recommendations recommendationsPromise={recommendations} />
      </Suspense>
    </div>
  );
}

function Recommendations({ recommendationsPromise }) {
  const { data } = useAsyncData(recommendationsPromise);
  return (
    <ul>
      {data.map(item => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  );
}

Использование Suspense позволяет централизованно управлять состоянием загрузки, упрощая рендеринг отложенных данных и минимизируя boilerplate код.

Рекомендации по оптимизации

  • Деление маршрутов: Разделять крупные страницы на дочерние маршруты для более точного контроля над загрузкой данных.
  • Приоритет данных: Всегда загружать сначала критичные данные, отложенные — позже.
  • Показ индикаторов загрузки: Для улучшения UX использовать скелетоны и placeholders.
  • Кэширование: Отложенные данные можно кэшировать на клиенте для повторного использования при навигации между маршрутами.
  • Асинхронные компоненты: Использовать React.lazy для отложенной загрузки тяжелых компонентов вместе с отложенными данными.

Взаимодействие с сервером

Для некритичных данных рекомендуется использовать отдельные API-запросы, которые не блокируют первичный рендер. Также полезно применять стратегию prefetching, когда данные загружаются заранее при наведении на ссылку или при прогнозируемой навигации, что уменьшает задержку при переходе на маршрут с отложенными данными.

Заключение по подходу

Отложенная загрузка некритичных данных в TanStack Router позволяет создавать быстрые и отзывчивые интерфейсы. Комбинация defer, useAsyncData и React Suspense дает полный контроль над асинхронными потоками данных, позволяя отображать критичный контент мгновенно, а менее важный — постепенно, без блокировки интерфейса. Такой подход особенно полезен в крупных SPA и PWA, где оптимизация загрузки напрямую влияет на пользовательский опыт и показатели производительности.