Tree shaking в JavaScript - как работает и как правильно настроить

28 января 2026
Автор

Олег Марков

Введение

Tree shaking в современном фронтенд-разработке стал одним из ключевых инструментов оптимизации. Эта техника позволяет удалить из финального бандла весь код, который фактически нигде не используется. В результате снижается вес JavaScript-файлов, ускоряется загрузка приложения и уменьшается время выполнения.

Давайте разберем, что именно такое Tree shaking, почему он появился именно с модулями ES6, как он работает внутри сборщиков и что нужно делать вам, чтобы он действительно приносил пользу, а не существовал только на слайдах из презентаций.

Вы увидите практические примеры конфигураций для Webpack, Rollup и ESBuild, а также реальные кейсы: почему иногда Tree shaking “не срабатывает”, как правильно писать код и публиковать библиотеки, чтобы оптимизация проходила максимально агрессивно.

Что такое Tree shaking и откуда взялся термин

Интуитивное объяснение

Представьте дерево: ствол, ветки и листья. При сборке JavaScript-приложения все ваши файлы, модули и функции образуют условное “дерево зависимостей”. Когда вы импортируете что-то из модуля, сборщик считает, что эта часть кода “нужна” и оставляет ее. Остальные, неиспользуемые части могут быть “стряхнуты” с дерева, как сухие листья.

Смотрите, что происходит на практике:

  • у вас есть модуль с десятью функциями;
  • вы импортируете только одну из них;
  • сборщик с поддержкой Tree shaking оставляет только эту функцию, а остальные девять удаляет из финального бандла.

Почему Tree shaking связан с ES-модулями

Ключевой момент: Tree shaking корректно работает только с “статическим анализом” кода. Это означает, что сборщик должен заранее, на этапе сборки, понять:

  • какие функции где объявлены;
  • какие из них точно импортируются;
  • какие экспорты точно не используются.

С модулями CommonJS (require, module.exports) это сделать сложно, потому что импорты и экспорты могут быть динамическими, зависящими от условий, и сборщик не может гарантировать их структуру.

ES-модули (import и export) устроены по-другому:

  • структура импортов и экспортов статична;
  • имена привязаны на этапе компиляции;
  • нельзя динамически изменить, что именно экспортируется.

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

Мертвая кодовая ветка против Tree shaking

Иногда путают Tree shaking и удаление “мертвого кода” (dead code elimination). Разница такая:

  • Dead code elimination — это оптимизация на уровне JavaScript-движка или минификатора. Она удаляет код, который точно не будет выполнен (например, ветку if (false) { ... }).
  • Tree shaking — удаляет неиспользуемые экспорты модулей, даже если они теоретически могли бы выполниться, если бы были импортированы.

Обычно Tree shaking и dead code elimination работают вместе. Сначала сборщик отмечает неиспользуемые экспорты, затем минификатор удаляет связанные с ними участки кода.

Основные принципы работы Tree shaking

Статический анализ импортов и экспортов

Давайте посмотрим на простой пример:

// math.js
export function add(a, b) {
  // Здесь мы складываем два числа
  return a + b
}

export function multiply(a, b) {
  // Здесь мы перемножаем два числа
  return a * b
}
// index.js
import { add } from './math.js'

// Здесь мы используем только функцию add
console.log(add(2, 3))

Если сборщик поддерживает Tree shaking и правильно настроен, в итоговый бандл попадет только функция add. Функция multiply будет удалена, потому что:

  • она экспортируется;
  • но нигде не импортируется;
  • значит, она “мертвая” с точки зрения графа зависимостей.

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

// Плохой для Tree shaking вариант
import * as math from './math.js'

console.log(math.add(2, 3))

Многие сборщики в таком случае “перестрахуются” и оставят весь код модуля math.js, потому что не всегда могут доказать, что вы не будете использовать math.multiply где-то еще.

Side effects и почему они ломают Tree shaking

Один из главных врагов Tree shaking — побочные эффекты (side effects). Побочный эффект — это любой код, который что-то меняет вне своей области, даже если результат не присваивается никуда напрямую. Примеры:

  • модуль при импорте что-то логирует в консоль;
  • модуль изменяет глобальный объект window;
  • модуль регистрирует что-то в глобальном реестре.

Смотрите на пример:

// logger.js
console.log('Логгер инициализирован') // Побочный эффект

export function log(message) {
  // Здесь мы выводим сообщение в консоль
  console.log(message)
}
// index.js
// Здесь мы нигде не используем log, но просто импортируем модуль
import './logger.js'

Даже если вы нигде не вызываете log, сам импорт logger.js нужен, потому что важен побочный эффект — вывод “Логгер инициализирован”. Сборщик не имеет права удалить этот импорт.

Отсюда два важных вывода:

  1. Если модуль имеет побочные эффекты, его нельзя “стряхнуть” целиком.
  2. Если вы используете стороннюю библиотеку, в которой инкапсулированы побочные эффекты, Tree shaking для нее может быть сильно ограничен.

Marking sideEffects: false и consequences

Многие сборщики позволяют разработчику явно сказать: “мой пакет не имеет побочных эффектов при импорте” или “побочные эффекты только в определенных файлах”. Это делается через поле sideEffects в package.json.

Например:

{
  "name": "my-lib",
  "version": "1.0.0",
  "module": "dist/index.esm.js",
  "sideEffects": false
}

Комментарий к этому примеру:

// sideEffects: false говорит сборщику - при импорте этого пакета // никакого кода с побочными эффектами не выполняется // значит, можно безопасно удалять неиспользуемые импорты

Если вы нарушите это обещание (у вас есть модуль, который что-то выполняет при импорте), а при этом указали sideEffects: false, то сборщик может удалить нужный код, и вы получите баг в рантайме.

Поэтому:

  • если вы пишете библиотеку, убедитесь, что действительно нет побочных эффектов при импорте, прежде чем ставить sideEffects: false;
  • если побочные эффекты есть только в отдельных файлах, укажите это явно с помощью массива путей.
{
  "sideEffects": [
    "./src/polyfills.js",
    "./src/styles.css"
  ]
}

Комментарий:

// В этом случае сборщик может смело удалять почти все файлы // кроме тех, что перечислены в sideEffects // Они всегда должны оставаться, даже если импорты выглядят "неиспользуемыми"

Tree shaking в Webpack

Базовые условия, чтобы Tree shaking заработал

Чтобы Webpack вообще смог применять Tree shaking, должны выполняться несколько условий:

  1. Вы используете ES-модули (import и export).
  2. В конфигурации установлен режим production (или включена оптимизация вручную).
  3. Код минифицируется с помощью Terser или другого минификатора, который умеет удалять неиспользуемый код.
  4. Правильно настроено поле sideEffects для пакетов.

Простейший пример конфигурации:

// webpack.config.js
const path = require('path')

module.exports = {
  mode: 'production', // В продакшн-режиме Webpack включает оптимизации
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    // Здесь мы указываем папку для сборки
    path: path.resolve(__dirname, 'dist')
  },
  optimization: {
    usedExports: true, // Помечает используемые экспорты
    // В режиме production Webpack автоматически включает minimizer с Terser
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        // Здесь мы подключаем Babel для транспиляции
        use: 'babel-loader'
      }
    ]
  }
}

В mode: 'production' опция usedExports включается автоматически, но здесь я указываю ее явно, чтобы вам было понятнее, что именно отвечает за Tree shaking.

Пример: как Webpack удаляет неиспользуемый код

Давайте разберем минимальный пример.

// src/utils.js
export function needed() {
  // Эта функция нужна и будет использоваться
  return 'I am used'
}

export function notNeeded() {
  // Эта функция никогда не будет импортирована
  return 'I am unused'
}
// src/index.js
import { needed } from './utils.js'

console.log(needed()) // Выводим результат в консоль

Если вы соберете этот проект в режиме production, Webpack:

  • отметит экспорт needed как используемый;
  • отметит экспорт notNeeded как неиспользуемый;
  • при минификации Terser удалит код notNeeded.

Если вы откроете минифицированный bundle.js (желательно через source map), вы увидите, что функция notNeeded туда не попала.

Как sideEffects влияет на Webpack

Представьте, что у вас в проекте есть index.js, который просто переэкспортирует другие модули:

// src/index.js
export * from './buttons.js'
export * from './inputs.js'
export * from './modals.js'

Даже если вы в своем приложении используете только одну кнопку из buttons.js, Webpack может “подстраховаться” и включить в бандл код всех трех файлов, если он не уверен, есть ли у них побочные эффекты.

Чтобы Webpack мог более агрессивно встряхивать дерево экспорта, ему нужно знать, что ваши модули не содержат побочных эффектов. Для этого вы добавляете в package.json:

{
  "sideEffects": false
}

Комментарий:

// Теперь Webpack может смело удалять целые файлы, если их экспорты // не используются нигде в коде

Если же побочные эффекты есть только в отдельных модулях, вы указываете их в массиве:

{
  "sideEffects": [
    "./src/polyfills.js",
    "./src/global.css"
  ]
}

Webpack при этом:

  • никогда не удалит импорты polyfills.js и global.css;
  • будет свободно удалять остальные “модульные” файлы, если их экспорты не используются.

Типичные ошибки при Tree shaking в Webpack

Разберем несколько ситуаций, когда вы ожидаете Tree shaking, а он не срабатывает.

Ошибка 1. Использование require вместо import

// Плохой вариант
const utils = require('./utils')

// Здесь Webpack теряет статическую информацию об экспортах
console.log(utils.needed())

В этом случае Webpack не может безопасно “стряхнуть” неиспользуемые функции из utils, потому что require работает динамически. Вы можете позже обратиться к utils.notNeeded, и сборщик об этом не узнает.

Решение:

// Хороший вариант
import { needed } from './utils.js'

console.log(needed())

Ошибка 2. Динамические импорты с переменной

// Плохой вариант
const moduleName = './utils'
import(moduleName).then(mod => {
  // Что именно здесь будет использоваться - сборщик не знает
  console.log(mod)
})

Сборщику сложно анализировать такой код. Лучше использовать явные пути:

// Лучше
import('./utils.js').then(mod => {
  // Здесь хотя бы понятно, какой модуль именно загружается
  console.log(mod.needed())
})

Ошибка 3. Babel ломает модули

Если Babel перенастроен так, что он преобразует ES-модули в CommonJS (использует @babel/preset-env с modules: 'commonjs' или без указания modules в некоторых конфигурациях), то на выходе для Webpack модули уже не статические ES-модули.

Пример правильной конфигурации Babel:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false // Важно - оставляем ES-модули для сборщика
      }
    ]
  ]
}

Комментарий:

// modules: false говорит Babel - не трогай import и export // пусть ими занимается Webpack или другой сборщик

Если вы забудете про modules: false, Babel превратит import в require, и Webpack потеряет возможность нормально делать Tree shaking.

Tree shaking в Rollup

Почему Rollup считается “эталоном” Tree shaking

Rollup изначально проектировался именно как модульный бандлер с фокусом на Tree shaking. В отличие от Webpack, который долгое время поддерживал CommonJS и множество форматов, Rollup делает ставку на ES-модули и работает с ними очень агрессивно.

В результате:

  • Rollup может удалять не только неиспользуемые функции, но и “расползающийся” код внутри модулей;
  • он аккуратно анализирует побочные эффекты;
  • бандлы получаются очень компактными, особенно для библиотек.

Минимальная конфигурация Rollup с Tree shaking

Давайте посмотрим простой пример.

// rollup.config.js
import { defineConfig } from 'rollup'
import babel from '@rollup/plugin-babel'

export default defineConfig({
  input: 'src/index.js', // Точка входа
  output: {
    file: 'dist/bundle.js',
    format: 'esm' // Формат выхода - ES-модули
  },
  plugins: [
    babel({
      babelHelpers: 'bundled',
      // Здесь мы указываем Babel использовать наш .babelrc или настройки
      exclude: 'node_modules/**'
    })
  ],
  treeshake: {
    moduleSideEffects: false
    // moduleSideEffects позволяет указать, что модули без явных побочных эффектов
    // можно обрезать максимально агрессивно
  }
})

По умолчанию Rollup уже включает Tree shaking. Опция treeshake позволяет контролировать его поведение тоньше:

  • treeshake: true — включить Tree shaking;
  • moduleSideEffects: false — считать, что модули не имеют побочных эффектов (аналог sideEffects: false, но на уровне конфигурации Rollup);
  • либо можно указывать функции и паттерны.

Пример, как Rollup удаляет неиспользуемые части

Давайте разберем модуль, где внутри функции есть “мертвые” части:

// src/math.js
export function compute(x) {
  // Здесь мы рассчитываем значение x * 2
  const result = x * 2

  // Этот блок никогда не будет выполнен
  if (false) {
    console.log('Этот код никогда не выполнится')
  }

  return result
}
// src/index.js
import { compute } from './math.js'

console.log(compute(10))

Rollup:

  1. Анализирует модуль math.js.
  2. Понимает, что if (false) — мертвая ветка.
  3. Удаляет ее даже до стадии минификации.

Минификатор затем еще сильнее оптимизирует код, но уже получает “очищенную” версию.

moduleSideEffects и точный контроль

Иногда в проекте встречаются модули с побочными эффектами в виде вызовов, которые вы хотите контролировать. Rollup позволяет указать moduleSideEffects как:

  • false — считать, что модуль не имеет побочных эффектов;
  • true — всегда считать, что побочные эффекты есть;
  • массив или функцию — для тонкой настройки.

Пример:

// rollup.config.js (фрагмент)
export default {
  // ...
  treeshake: {
    moduleSideEffects: id => {
      // Здесь id - путь к модулю
      // Укажем, что только polyfills.js имеет побочные эффекты
      if (id.endsWith('polyfills.js')) {
        return true
      }
      return false
    }
  }
}

Комментарий:

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

Rollup как инструмент для библиотек

Если вы пишете библиотеку, которая будет устанавливаться через npm, Rollup часто оказывается удобнее Webpack:

  • он генерирует чистый ESM или UMD;
  • Tree shaking работает “из коробки”;
  • можно сделать несколько форматов (cjs, esm) в одном конфиге.

Пример настройки выходов:

// rollup.config.js (фрагмент)
export default {
  input: 'src/index.js',
  output: [
    {
      file: 'dist/index.cjs',
      format: 'cjs' // CommonJS для Node.js
    },
    {
      file: 'dist/index.esm.js',
      format: 'esm' // ES-модули для Tree shaking в сборщиках
    }
  ]
}

Комментарий:

// Так вы публикуете сразу два формата // Пользователи браузерных сборщиков будут использовать ESM // и смогут получать все преимущества Tree shaking

Tree shaking в ESBuild

Особенности ESBuild

ESBuild — это очень быстрый сборщик и транспайлер, написанный на Go. Он поддерживает Tree shaking, но делает это несколько иначе, чем Webpack и Rollup:

  • он по умолчанию предполагает, что код может содержать побочные эффекты;
  • чтобы Tree shaking работал агрессивно, нужно включать формат ESM и режим bundling;
  • ESBuild учитывает поле sideEffects в package.json.

Пример базовой конфигурации через CLI:

esbuild src/index.js \
  --bundle \
  --format=esm \
  --minify \
  --outfile=dist/bundle.js

Комментарий:

// --bundle - собираем все модули в один файл // --format=esm - оставляем формат ES-модулей // --minify - включаем минификацию и удаление неиспользуемого кода

Пример с Tree shaking в ESBuild

Давайте разберем следующий код:

// src/helpers.js
export function used() {
  // Эта функция используется в коде
  return 'used'
}

export function unused() {
  // Эта функция нигде не вызывается
  return 'unused'
}
// src/index.js
import { used } from './helpers.js'

console.log(used())

При сборке с ESBuild:

  • экспорт used останется;
  • экспорт unused будет удален из итогового бандла (если нет побочных эффектов в модуле).

Учет поля sideEffects ESBuild

ESBuild, как и Webpack, читает поле sideEffects в package.json, чтобы понять, можно ли удалять импорты, которые кажутся неиспользуемыми.

Если у вас есть пакет с:

{
  "sideEffects": false
}

то ESBuild может удалять даже “голые” импорты без использования, если считает, что побочных эффектов там нет.

Например:

import './polyfills.js' // Если sideEffects: false, ESBuild может удалить этот импорт

Чтобы этого не произошло, если в polyfills.js действительно есть побочные эффекты, вам нужно:

  • либо не ставить sideEffects: false;
  • либо явно указать polyfills.js в массиве sideEffects.
{
  "sideEffects": [
    "./src/polyfills.js"
  ]
}

Комментарий:

// Так вы защищаете критичный модуль с полифилами от удаления // даже если его импорт выглядит "неиспользуемым" в коде

Как писать код, который хорошо “стряхивается”

Используйте только ES-модули

Главное правило: пишите модули с использованием import и export. Избегайте:

  • require;
  • module.exports;
  • динамических import с переменными путями.

Правильные примеры:

// utils.js
export function formatDate(date) {
  // Здесь мы форматируем дату в строку
  return date.toISOString()
}

export function parseDate(str) {
  // Здесь мы разбираем строку в объект Date
  return new Date(str)
}
// main.js
import { formatDate } from './utils.js'

const now = new Date()
// Здесь мы выводим отформатированную дату
console.log(formatDate(now))

При таком подходе Tree shaking может спокойно удалить parseDate, если она нигде не используется.

Избегайте “собирающих” модулей без необходимости

Часто разработчики создают index.js, который переэкспортирует все подряд:

// components/index.js
export * from './Button.js'
export * from './Input.js'
export * from './Modal.js'
export * from './Dropdown.js'

А затем в приложении:

// main.js
import { Button } from './components/index.js'

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

// Рекомендация для максимального Tree shaking
import { Button } from './components/Button.js'

или

import Button from './components/Button.js'

Тогда сборщик точно знает, что индексный модуль components/index.js вообще не нужен, и может его выкинуть.

Не смешивайте побочные эффекты и чистые функции

Хорошая практика — разделять модули:

  • на чистые, без побочных эффектов, которые содержат только функции и классы;
  • и на модули инициализации, которые что-то делают при импорте (инициализируют глобальные слушатели, регистрируют плагины).

Пример:

// analytics/core.js
export function trackEvent(name, payload) {
  // Здесь мы отправляем событие аналитики на сервер
  console.log('Send event', name, payload)
}
// analytics/init.js
import { trackEvent } from './core.js'

// Здесь мы регистрируем обработчик кликов
document.addEventListener('click', event => {
  trackEvent('click', { x: event.clientX, y: event.clientY })
})

Если вы хотите Tree shaking только для analytics/core.js, а init.js должен всегда выполняться при явном импорте, вы можете:

  • указать init.js в sideEffects;
  • а core.js оставить “чистым” и пригодным для Tree shaking.
{
  "sideEffects": [
    "./src/analytics/init.js"
  ]
}

Комментарий:

// Теперь импорты из core.js будут анализироваться и обрезаться // а init.js будет всегда считаться модулем с побочными эффектами

Избегайте “скрытых” побочных эффектов

Иногда внутри функций вы можете делать что-то, что сложно предсказать сборщику:

  • изменять глобальные переменные;
  • модифицировать прототипы встроенных объектов;
  • регистрировать что-то в глобальных синглтонах.

С точки зрения Tree shaking это не всегда критично, но такие практики ухудшают прогнозируемость кода. Если сборщик не может точно сказать, влияют ли эти операции на остальную часть программы, он будет консервативнее в оптимизациях.

Лучше:

  • делать функции чистыми, где это возможно;
  • побочные эффекты помещать в явно помеченные “инициализационные” модули.

Tree shaking и публикация библиотек

Как правильно готовить библиотеку к Tree shaking

Когда вы публикуете библиотеку в npm и хотите, чтобы пользователи могли “стряхивать” неиспользуемые части:

  1. Обязательно предоставляйте сборку в формате ES-модулей.
  2. Указывайте поле module в package.json.
  3. Настраивайте sideEffects.

Пример package.json:

{
  "name": "awesome-lib",
  "version": "1.0.0",
  "main": "dist/index.cjs",     // CommonJS-версия для Node.js
  "module": "dist/index.esm.js", // ESM-версия для Tree shaking
  "sideEffects": false
}

Комментарий:

// main - точка входа для окружений, ожидающих CommonJS // module - точка входа для сборщиков, которые умеют Tree shaking // sideEffects: false - библиотека не выполняет побочных эффектов при импорте

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

{
  "sideEffects": [
    "./dist/polyfills.js",
    "./dist/registerGlobal.js"
  ]
}

Структура экспорта для лучшего Tree shaking

Старайтесь экспортировать ваши функции, классы и компоненты “поштучно”, а не одним огромным объектом.

Меньше подходит:

// index.esm.js
const lib = {
  a() {
    // ...
  },
  b() {
    // ...
  }
}

export default lib

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

Лучше:

// index.esm.js
export function a() {
  // ...
}

export function b() {
  // ...
}

Теперь пользователь может написать:

import { a } from 'awesome-lib'

// Здесь будет импортирована только функция a
a()

И Tree shaking удалит функцию b, если она не используется.

Избегайте “магических” индексных файлов

Часто для удобства делают:

// src/index.js
import './polyfills.js' // Побочный эффект при импорте
export * from './core.js'

Если вы укажете module: "dist/index.esm.js" и sideEffects: false, то пользователь, который импортирует всего одну функцию из вашей библиотеки, неожиданно получит полифилы, которые вы не планировали “вешать” автоматически.

Лучший подход:

  • разделить index с полифилами и index без полифилов;
  • либо вынести полифилы в отдельный модуль, который пользователь импортирует явно.
// src/index.js
export * from './core.js'
// src/with-polyfills.js
import './polyfills.js'
export * from './core.js'

Тогда в package.json вы укажете:

{
  "module": "dist/index.esm.js",
  "exports": {
    ".": "./dist/index.esm.js",
    "./with-polyfills": "./dist/with-polyfills.esm.js"
  }
}

Комментарий:

// Пользователь сам решает, нужен ли ему вариант с полифилами // А основной вход остается "чистым" и хорошо поддающимся Tree shaking

Заключение

Tree shaking — это не одна волшебная галочка в конфигурации сборщика, а результат совместной работы:

  • архитектуры модулей в вашем коде;
  • формата экспорта библиотек;
  • настройки сборщика (Webpack, Rollup, ESBuild);
  • корректного описания побочных эффектов через sideEffects и аналогичные опции.

Если вы:

  • используете ES-модули;
  • избегаете лишних побочных эффектов в модулях;
  • правильно настраиваете Babel (modules: false);
  • аккуратно описываете sideEffects в package.json,

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

Полезно периодически проверять итоговые бандлы с помощью инструментов анализа (webpack-bundle-analyzer, source-map-explorer и аналогичных). Так вы на практике увидите, какие модули “не стряхиваются” и почему, и сможете шаг за шагом улучшать структуру кода.

Частозадаваемые технические вопросы по теме Tree shaking

Как понять, что Tree shaking действительно сработал в моем проекте

  1. Соберите проект в режиме production.
  2. Подключите анализатор бандла:
    • для Webpack используйте webpack-bundle-analyzer;
    • для других сборщиков можно использовать source-map-explorer.
  3. Посмотрите, попадают ли в бандл модули и функции, которые вы ожидаете удалить.
  4. Если неиспользуемый код остался, проверьте:
    • используете ли вы ES-модули;
    • не ломает ли Babel формат модулей;
    • как настроено поле sideEffects.

Почему при использовании TypeScript Tree shaking иногда не работает

  1. Проверьте компилятор TypeScript:
    • в tsconfig.json установите "module": "ESNext" или "ES2015";
    • избегайте "module": "CommonJS".
  2. Убедитесь, что сборщик (Webpack, Rollup, ESBuild) обрабатывает уже ESM-код, а не CommonJS.
  3. Если используете ts-loader или babel-loader, посмотрите, не преобразуют ли они модули в CommonJS.

Можно ли добиться Tree shaking с CommonJS модулями

Полноценного Tree shaking для CommonJS добиться нельзя, но можно частично:

  1. Некоторые сборщики пытаются делать “lazy evaluation”, но это нестабильно.
  2. Рекомендуется постепенно переписывать модули на ES-модули.
  3. Если библиотека только в CommonJS, попробуйте найти ESM-версию или форк.

Как быть с CSS и Tree shaking

  1. CSS сам по себе не поддается Tree shaking так же, как JS.
  2. Некоторые инструменты (например, PurgeCSS, Tailwind JIT) реализуют “аналог” Tree shaking для стилей:
    • анализируют HTML и JS;
    • удаляют неиспользуемые классы из итогового CSS.
  3. Для Webpack можно подключать плагины, удаляющие неиспользуемые правила стилей на основе анализа разметки.

Почему импорт по умолчанию мешает Tree shaking как экспорта по частям

  1. При default export часто делают “объект с кучей методов”:
    • сборщику сложно удалить отдельные свойства такого объекта.
  2. Лучше экспортировать функции поименно:
    • export function a() {}
    • export function b() {}
  3. Импортировать тоже поименно:
    • import { a } from 'lib' Тогда сборщик увидит, что b не используется, и удалит ее.
Стрелочка влевоVirtual Scrolling - как эффективно отображать большие спискиОптимизация производительности в Go - практический разборСтрелочка вправо

Постройте личный план изучения Vue до уровня Middle — бесплатно!

Vue — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Vuex - полное руководство по управлению состоянием во Vue приложенияхРеактивные ссылки ref - полный разбор для разработчиковРеактивные объекты reactive-objects - подробное руководство с примерамиРеактивные переменные - концепция reactive и практические примерыМеханизм Provide Inject - как он работает и когда применятьЛокальное состояние local state в веб разработкеPinia современный менеджер состояния для VueГлобальное состояние в приложениях - global state
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsПонимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Гайд по defineEmits на Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
Функция append в Go GolangОтображение компонента mounted - практическое руководствоХуки жизненного цикла компонентов - полное руководство для разработчиковУничтожение компонента destroyed - как правильно очищать ресурсы и подпискиИнициализация данных в состоянии created - как и когда подготавливать данные в приложенииОбновление компонента beforeUpdate во VueМонтирование компонента - хук beforeMount в VueРазрушение компонента во Vue - beforeDestroy и beforeUnmountСоздание экземпляра beforeCreate - полный разбор жизненного цикла
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиИнтеграция Node.js и Vue.js для разработки приложенийКак обрабатывать async операции с Promise во VueПримеры использования JSX во VueРуководство по интеграции Vue js в NET проектыМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во VueГайд по импорту и регистрации компонентов на Vue
Слоты компонента - концепция и практическое использованиеРегистрация компонентов component-registration в приложениях с внедрением зависимостейProps компонента в React - полный разбор с примерамиФункциональные компоненты в React - функциональный подход к построению интерфейсовСобытия компонента - events в современных интерфейсахДинамические компоненты - dynamic-componentsСоздание компонента component - практическое руководствоАсинхронные компоненты async-components - практическое руководство
Наблюдатели watchers - от паттерна до практических реализацийУправление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsШаблоны Vue templates - практическое руководство для разработчиковИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueРеактивность Vue reactivity - как это работает под капотом и как этим пользоватьсяПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueОпции компонента в Go - паттерн component-optionsРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreВычисляемые свойства computed во Vue.jsСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueОсновы Vue - vue-basics для уверенного стартаИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueТелепортация - архитектура и реализация в серверных приложенияхРабота с teleport для управления DOM во VueSuspense в React - управление асинхронными данными и ленивой загрузкойПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиПлагины Vue vue-plugins - полное практическое руководствоРуководство по nextTick для работы с DOMМиксины - mixins в современном программированииJSX в Vue с использованием плагина vue-jsxСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьКастомные элементы - Custom Elements в современном JavaScriptИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Vue 3 и Pinia

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий