Параллельная загрузка данных

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


Loader-функции и их роль

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

const userLoader = async ({ params }) => {
  const response = await fetch(`/api/users/${params.userId}`);
  return response.json();
};

const postsLoader = async () => {
  const response = await fetch('/api/posts');
  return response.json();
};

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


Параллельная загрузка с помощью Promise.all

Для выполнения нескольких асинхронных запросов параллельно используется стандартный механизм JavaScript Promise.all. Это позволяет запускать загрузку данных одновременно, не дожидаясь завершения предыдущего запроса:

const dashboardLoader = async () => {
  const [users, posts, comments] = await Promise.all([
    fetch('/api/users').then(res => res.json()),
    fetch('/api/posts').then(res => res.json()),
    fetch('/api/comments').then(res => res.json()),
  ]);

  return { users, posts, comments };
};

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


Интеграция параллельных загрузок в маршруты

В TanStack Router маршруты определяются с помощью createRouter и могут включать loader-функции на любом уровне вложенности:

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

const dashboardRoute = createRoute({
  path: '/dashboard',
  loader: dashboardLoader,
  component: DashboardPage,
});

const router = createRouter({
  routes: [dashboardRoute],
});

Поскольку loader выполняется до рендеринга компонента, возвращаемые данные автоматически передаются в компонент через useLoaderData:

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

function DashboardPage() {
  const { users, posts, comments } = useLoaderData();

  return (
    <>
      <UserList users={users} />
      <PostList posts={posts} />
      <CommentList comments={comments} />
    </>
  );
}

Это обеспечивает единообразный способ работы с асинхронными данными и упрощает управление состоянием.


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

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

const usersRoute = createRoute({
  path: 'users',
  loader: async () => fetch('/api/users').then(res => res.json()),
  component: UsersPage,
});

const postsRoute = createRoute({
  path: 'posts',
  loader: async () => fetch('/api/posts').then(res => res.json()),
  component: PostsPage,
});

const dashboardRoute = createRoute({
  path: '/dashboard',
  children: [usersRoute, postsRoute],
});

При переходе на /dashboard/users выполняется только loader для пользователей, а при переходе на /dashboard/posts — только loader для постов. Если нужно отобразить сразу все данные на главной странице дашборда, можно объединить загрузки через Promise.all, как показано выше.


Оптимизация параллельных загрузок

  1. Избегать избыточных запросов — перед запуском загрузок проверять, есть ли уже закэшированные данные.
  2. Обрабатывать ошибки отдельно — при параллельных запросах лучше использовать Promise.allSettled, чтобы даже при частичных ошибках можно было отобразить доступные данные:
const dashboardLoader = async () => {
  const results = await Promise.allSettled([
    fetch('/api/users').then(res => res.json()),
    fetch('/api/posts').then(res => res.json()),
    fetch('/api/comments').then(res => res.json()),
  ]);

  return {
    users: results[0].status === 'fulfilled' ? results[0].value : [],
    posts: results[1].status === 'fulfilled' ? results[1].value : [],
    comments: results[2].status === 'fulfilled' ? results[2].value : [],
  };
};
  1. Кэширование на уровне маршрутов — loader можно интегрировать с TanStack Query или другим кэшированием, чтобы повторные переходы не запускали лишние сетевые запросы.

Выводы по параллельной загрузке

  • Параллельные загрузки ускоряют время рендеринга страниц с множеством данных.
  • Loader-функции являются основным инструментом для асинхронной загрузки данных в TanStack Router.
  • Вложенные маршруты позволяют делить страницу на независимые сегменты с отдельной загрузкой.
  • Использование Promise.all и Promise.allSettled обеспечивает гибкое управление множественными асинхронными запросами и обработку ошибок.

Эффективная параллельная загрузка данных — ключевой элемент производительных и отзывчивых веб-приложений с TanStack Router.