Skip to main content

Tree shaking

Tree shaking це процес видалення невикористаного коду з фінального JavaScript-бандла. Назва дослівно означає «струшування дерева»: уявіть, що код проєкту це дерево, а зайві, мертві гілки (функції, модулі, імпорти) просто відпадають під час збірки. Менше коду означає швидше завантаження і кращу продуктивність.

Теорія

TL;DR

  • Якщо функція, змінна або імпорт ніде не використовуються, збирач (Webpack, Rollup, Vite) видалить цей код з підсумкового файлу.
  • Тримається на ES Modules: import / export статичні, тому їх можна проаналізувати на етапі збірки.
  • CommonJS (require) tree shaking не підтримує.
  • Побічні ефекти блокують видалення модуля, доки package.json не скаже "sideEffects": false.
  • У Rollup, Vite та esbuild працює з коробки, у Webpack вмикається в production-режимі.

Швидкий приклад

javascript
// utils.js export function add(a, b) { return a + b; } export function sub(a, b) { return a - b; } export function mul(a, b) { return a * b; } export function div(a, b) { return a / b; } // main.js import { add } from './utils.js'; console.log(add(2, 3));

Без tree shaking збирач покладе в bundle.js усі функції (add, sub, mul, div), навіть якщо реально використовується лише add(). З увімкненим tree shaking (production-режим Webpack, Rollup або Vite) у підсумковому бандлі залишиться тільки add(). У результаті розмір бандла менший, завантаження швидше, а парсинг і виконання коду теж швидші.

Як це працює технічно

Tree shaking спирається на три речі:

  1. ES Modules (ESM), тобто import / export: на відміну від require, ESM-імпорти статичні й аналізуються на етапі збірки;
  2. статичний аналіз залежностей: збирач знає, які саме експорти реально використовуються;
  3. видалення мертвого коду через оптимізацію dead code elimination.
javascript
// імпорт статичний, тому збирач бачить, що використовується лише add import { add } from './utils.js';

А от CommonJS не підтримує tree shaking:

javascript
const utils = require('./utils'); // збирач не може точно знати, що використовується

Коли tree shaking не спрацює

Tree shaking не працює, якщо:

  • ви використовуєте CommonJS (require) замість ES Modules (import / export);
  • код має побічні ефекти, наприклад виконується під час імпорту;
  • бібліотека не вказала "sideEffects": false у package.json;
  • ви пишете динамічні імпорти або звертаєтеся до властивостей через змінні.

Приклад із побічним ефектом:

javascript
// side.js console.log("This code runs on import!"); export const x = 42;

Навіть якщо x не використовується, весь файл не буде видалено, бо під час імпорту виконується console.log(), а це побічний ефект.

Налаштування у Webpack, Rollup і Vite

Webpack. Tree shaking вмикається автоматично в production-режимі, якщо в package.json є:

json
{ "sideEffects": false }

Замість false можна дати список файлів, у яких побічні ефекти справді є.

Rollup. Tree shaking увімкнений за замовчуванням. Історично Rollup був першим збирачем, який це впровадив.

Vite та esbuild. Працює з коробки, бо обидва побудовані навколо ESM, а Vite ще й використовує Rollup для production-збірки.

Приклад результату

До оптимізації бандл важив 120 KB, після tree shaking, 42 KB.

ПунктОпис
Що робитьВидаляє невикористаний код з підсумкового бандла
На чому базуєтьсяESM (import / export) і статичний аналіз
Працює вWebpack, Rollup, Vite, Parcel
Не працює зrequire(), побічними ефектами
МетаЗменшити розмір і пришвидшити завантаження

Типові помилки

  • Чекати tree shaking від CommonJS-залежності. Якщо бібліотека публікує лише CJS-збірку, збирач не зможе нічого відрізати; шукайте пакет з полем module або exports на ESM.
  • Імпортувати весь пакет заради однієї функції. import _ from 'lodash' тягне все; import debounce from 'lodash-es/debounce' дає збирачу шанс.
  • Ставити "sideEffects": false наосліп. Якщо у пакеті є файли з реальними побічними ефектами, наприклад глобальні поліфіли або імпорт CSS, вони мовчки зникнуть з бандла.
  • Перевіряти результат у dev-режимі. У Webpack tree shaking працює лише в production-збірці, тож розмір треба міряти саме на ній.
  • Звертатися до експортів через змінну. utils[name]() робить статичний аналіз неможливим, і збирач залишає все.
  • Плутати tree shaking з мініфікацією. Мініфікатор скорочує наявний код, а tree shaking взагалі не дає невикористаному коду потрапити в бандл.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.