Skip to main content

Як працює "tree shaking"?

Що таке Tree Shaking

Tree shaking - це процес аналізу імпортів/експортів модулів і видалення тих, які не використовуються у фінальному коді.

Простіше кажучи:

Якщо функція, клас чи змінна ніколи не викликається / не імпортується, збирач просто викидає їх з підсумкового бандла.

Назва метафорична: дерево = граф залежностей проєкту, shaking = «струшуємо» невикористані гілки.


Приклад без tree shaking

javascript
// math.js export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; } // app.js import { add } from './math.js'; console.log(add(2, 3));

Без tree shaking у бандл потрапить і add, і multiply, хоча multiply не використовується.


Приклад з tree shaking

Сучасний збирач (Rollup, Webpack 5+, ESBuild, Vite) проаналізує:

  • multiply ніде не імпортується,
  • отже, можна безпечно видалити.

У підсумковому бандлі залишиться лише:

javascript
function add(a,b){return a+b} console.log(add(2,3))

Умови, щоб tree shaking спрацював

Tree shaking працює лише з ES Modules (ESM), тому що вони статичні:

javascript
// ESM import { add } from './math.js'; // CommonJS (аналіз неможливий на етапі збирання) const math = require('./math');

Збирач має бачити всі імпорти/експорти на етапі компіляції, а не під час виконання (динамічні require() ламають оптимізацію).


Як збирач розуміє, що можна видалити

Tree shaking - це аналіз графа залежностей:

  1. Визначає "корені" - модулі, реально використовувані в застосунку (entry points);
  2. Будує граф імпортів;
  3. Видаляє все, що недосяжне з цих коренів.

"Dead Code Elimination" vs "Tree Shaking"

Tree shaking - логічний аналіз імпортів/експортів на рівні модулів. Dead code elimination (DCE) - подальший аналіз на рівні виразів (зазвичай робить Terser/UglifyJS).

Зазвичай ланцюжок такий:

javascript
Webpack / Rollup → позначають невикористані експорти ↓ Terser → фізично вирізає код

Приклад: побічні ефекти (side effects)

Tree shaking не видаляє код з побічними ефектами - те, що може змінити глобальний стан.

javascript
// utils.js console.log('модуль завантажено'); // побічний ефект! export const a = 1; export const b = 2;

Навіть якщо ти не імпортуєш a чи b, збирач не видалить модуль, якщо бачить побічний ефект (виклик console.log, зміну глобальної змінної тощо).


Підказка збирачу: package.json → sideEffects

У пакетах можна явно вказати, що код без побічних ефектів, тоді tree shaking працює агресивніше:

javascript
{ "name": "my-lib", "sideEffects": false }

або вибірково:

javascript
{ "sideEffects": [ "*.css", "./polyfills.js" ] }

Типові проблеми, через які tree shaking не працює

ПричинаЧому
Використовується CommonJS (require)немає статичного аналізу залежностей
Динамічні імпорти (import * as m) або require(variable)збирач не може передбачити, що саме імпортується
Побічні ефекти в модуляхїх не можна безпечно видалити
Плагіни або транспіляція ламають ESMнаприклад, Babel перетворює import/export на require
Не увімкнено mode: 'production' у Webpackу dev-режимі оптимізації вимкнені

Приклад: Webpack

javascript
// webpack.config.js module.exports = { mode: 'production', // вмикає tree-shaking + minify optimization: { usedExports: true, // позначає використані експорти } };

Якщо ти дивишся в bundle-analyzer, побачиш коментарі /* unused harmony export */ - це tree shaking у дії.


Приклад: Rollup (ідеальний випадок)

Rollup спочатку був побудований навколо tree shaking, тому він робить це найкраще:

javascript
// rollup.config.js export default { input: 'src/index.js', output: { file: 'dist/bundle.js', format: 'esm' } };

Rollup видаляє невикористані експорти і навіть "ланцюжки" функцій, якщо вони не викликаються.


Інтеграція з Vite / ESBuild

Vite і ESBuild завжди працюють з ESM і вбудовано підтримують tree shaking - нічого налаштовувати не потрібно.

Головне: не використовувати CommonJS і не мати прихованих побічних ефектів.


Підсумок

ЩоОпис
Tree shakingВидалення невикористаних імпортів/експортів
Працює лише зES Modules (import / export)
Не видаляєКод з побічними ефектами
НалаштуванняsideEffects: false у package.json, mode: production
ІнструментиWebpack ≥ 2, Rollup, ESBuild, Vite
МетаЗменшити розмір бандла без ручного видалення коду

Головна ідея:

Збирач будує "дерево залежностей" і "струшує" з нього все, що не використовується, тому "tree shaking".

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

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

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