Tree shaking

Tree shaking в TanStack Query — это важный аспект оптимизации размера бандла, особенно в современных фронтенд-приложениях, где каждые лишние килобайты напрямую влияют на скорость загрузки и взаимодействие с пользователем. Библиотека TanStack Query изначально проектировалась с учётом модульной архитектуры, позволяющей удалять неиспользуемый код на этапе сборки, однако фактическая эффективность tree shaking зависит не только от самой библиотеки, но и от конфигурации сборщика и способа импорта.

Основой tree shaking является использование ES Modules. TanStack Query полностью построена на ESM-экспортах, что позволяет статически анализировать зависимости и удалять неиспользуемые части кода.

Ключевая особенность заключается в том, что библиотека разделена на логические модули:

  • ядро работы с кэшом и query client
  • реактивные интеграции (React, Vue и др.)
  • утилитарные функции и внутренние хелперы
  • дополнительные плагины и расширения

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

Роль точек входа (entry points)

TanStack Query предоставляет несколько уровней импортов, и именно выбор entry point определяет итоговую способность к tree shaking.

Основные варианты импорта:

import { QueryClient } from '@tanstack/react-query'

или более специализированный:

import { QueryClient } from '@tanstack/react-query/core'

Второй вариант даёт более «чистую» зависимость, исключая React-специфичный слой.

Использование глубинных импортов позволяет:

  • исключить UI-адаптеры
  • уменьшить итоговый bundle size
  • убрать побочные зависимости (peer dependencies для React и Devtools)

Однако чрезмерное дробление импортов может привести к ухудшению читаемости кода и усложнению поддержки.

Побочные эффекты и их влияние

Одним из критических факторов для tree shaking является наличие или отсутствие side effects.

TanStack Query проектируется как библиотека с минимальными побочными эффектами. В package.json используется:

{
  "sideEffects": false
}

Это даёт сборщику сигнал, что модули можно безопасно удалять, если они не используются.

Однако важно учитывать:

  • некоторые окружения могут добавлять собственные side effects через polyfills
  • React Query Devtools содержит побочные эффекты и не всегда поддаётся полной оптимизации
  • условные импорты могут нарушить статический анализ

Разделение core и framework-адаптеров

Одним из ключевых факторов оптимизации является разделение ядра и адаптеров под конкретные фреймворки.

TanStack Query Core:

import { QueryClient } from '@tanstack/query-core'

React-обвязка:

import { useQuery } from '@tanstack/react-query'

Core-слой не содержит привязки к React и может использоваться в любых средах — от Node.js до Svelte и vanilla JS. Это позволяет:

  • полностью исключить React-код, если он не используется
  • уменьшить размер бандла в non-React окружениях
  • переиспользовать кэш и логику запросов между платформами

Tree shaking здесь работает особенно эффективно, так как core не содержит лишних UI-абстракций.

Оптимизация импортов и влияние barrel-файлов

Barrel-файлы (index.ts, re-export модули) часто ухудшают эффективность tree shaking, поскольку могут скрывать реальную структуру зависимостей.

TanStack Query минимизирует использование агрессивных barrel-экспортов, однако проблема может возникнуть на уровне приложения:

import { useQuery, useMutation } from '@tanstack/react-query'

Этот импорт остаётся оптимальным, но при создании собственных обёрток вида:

export * from './queries'

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

Рекомендуется придерживаться:

  • прямых импортов из библиотеки
  • избегания лишних re-export слоёв
  • отказа от промежуточных «index-only» модулей без необходимости

Влияние devtools на итоговый bundle

TanStack Query Devtools подключаются отдельно:

import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

Они являются наиболее частым источником увеличения размера бандла.

Tree shaking здесь работает ограниченно, поскольку devtools:

  • содержат UI-компоненты
  • используют условные эффекты
  • включают отладочную логику и форматирование данных

По этой причине их рекомендуется подключать только в development-сборках:

{process.env.NODE_ENV === 'development' && (
  <ReactQueryDevtools />
)}

Такой подход позволяет гарантировать исключение devtools из production bundle.

Side-effectful imports и динамическая загрузка

В некоторых случаях tree shaking не способен полностью удалить код из-за динамических импортов:

const module = await import('@tanstack/react-query')

Такая конструкция создаёт отдельный chunk, но не всегда гарантирует минимальный размер внутри него.

Также стоит учитывать:

  • динамические импорты отключают статический анализ
  • код может быть разделён на дополнительные чанки
  • повторное использование модулей может привести к дублированию runtime

TanStack Query совместима с code splitting стратегиями, но эффективность зависит от конфигурации bundler-а.

Webpack, Vite и Rollup: различия в tree shaking

Эффективность tree shaking напрямую зависит от инструмента сборки.

Webpack 5:

  • поддерживает полноценный ESM tree shaking
  • требует production mode для максимальной эффективности
  • чувствителен к sideEffects флагу

Vite:

  • использует Rollup под капотом
  • имеет более агрессивный static analysis
  • быстрее удаляет неиспользуемые экспорты

Rollup:

  • наиболее точный в tree shaking
  • минимизирует финальный bundle
  • предпочтителен для библиотек и SDK

TanStack Query показывает наилучшие результаты именно в Rollup/Vite окружении, где ESM используется без промежуточных преобразований.

Практика уменьшения bundle size через импортную стратегию

Tree shaking в TanStack Query можно усиливать через корректные импортные паттерны:

  • использование @tanstack/query-core вместо полного пакета, если React не нужен
  • исключение devtools из production
  • избегание глобальных namespace импортов
  • минимизация re-export слоёв в приложении

Пример оптимального подхода:

import { QueryClient, QueryCache } from '@tanstack/query-core'

или при React-использовании:

import { useQuery } from '@tanstack/react-query'

без дополнительных промежуточных абстракций.

Гранулярность модулей и будущее оптимизации

Современные версии TanStack Query продолжают движение в сторону более гранулярной модульности. Это выражается в:

  • разделении адаптеров под фреймворки
  • вынесении утилит в отдельные пакеты
  • минимизации связности между core и UI слоями

Такая архитектура делает tree shaking не просто возможностью сборщика, а частью дизайна библиотеки.

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

Ограничения tree shaking в реальных проектах

Несмотря на теоретическую эффективность, в реальных приложениях возникают ограничения:

  • косвенные зависимости через сторонние библиотеки
  • полифиллы, добавляющие побочные эффекты
  • неявные re-export цепочки
  • неправильные production настройки сборщика

TanStack Query в таких условиях может оставаться частично «неотрезанным», даже при корректной архитектуре импортов.

Основной фактор эффективности остаётся не библиотека, а дисциплина использования модулей и конфигурация сборки.