Як працює "tree shaking"?
Що таке Tree Shaking
Tree shaking - це процес аналізу імпортів/експортів модулів і видалення тих, які не використовуються у фінальному коді.
Простіше кажучи:
Якщо функція, клас чи змінна ніколи не викликається / не імпортується, збирач просто викидає їх з підсумкового бандла.
Назва метафорична: дерево = граф залежностей проєкту, shaking = «струшуємо» невикористані гілки.
Приклад без tree shaking
// 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ніде не імпортується,- отже, можна безпечно видалити.
У підсумковому бандлі залишиться лише:
function add(a,b){return a+b}
console.log(add(2,3))Умови, щоб tree shaking спрацював
Tree shaking працює лише з ES Modules (ESM), тому що вони статичні:
// ESM
import { add } from './math.js';
// CommonJS (аналіз неможливий на етапі збирання)
const math = require('./math');Збирач має бачити всі імпорти/експорти на етапі компіляції, а не під час виконання (динамічні require() ламають оптимізацію).
Як збирач розуміє, що можна видалити
Tree shaking - це аналіз графа залежностей:
- Визначає "корені" - модулі, реально використовувані в застосунку (entry points);
- Будує граф імпортів;
- Видаляє все, що недосяжне з цих коренів.
"Dead Code Elimination" vs "Tree Shaking"
Tree shaking - логічний аналіз імпортів/експортів на рівні модулів. Dead code elimination (DCE) - подальший аналіз на рівні виразів (зазвичай робить Terser/UglifyJS).
Зазвичай ланцюжок такий:
Webpack / Rollup → позначають невикористані експорти
↓
Terser → фізично вирізає кодПриклад: побічні ефекти (side effects)
Tree shaking не видаляє код з побічними ефектами - те, що може змінити глобальний стан.
// utils.js
console.log('модуль завантажено'); // побічний ефект!
export const a = 1;
export const b = 2;Навіть якщо ти не імпортуєш a чи b, збирач не видалить модуль,
якщо бачить побічний ефект (виклик console.log, зміну глобальної змінної тощо).
Підказка збирачу: package.json → sideEffects
У пакетах можна явно вказати, що код без побічних ефектів, тоді tree shaking працює агресивніше:
{
"name": "my-lib",
"sideEffects": false
}або вибірково:
{
"sideEffects": [
"*.css",
"./polyfills.js"
]
}Типові проблеми, через які tree shaking не працює
| Причина | Чому |
|---|---|
Використовується CommonJS (require) | немає статичного аналізу залежностей |
Динамічні імпорти (import * as m) або require(variable) | збирач не може передбачити, що саме імпортується |
| Побічні ефекти в модулях | їх не можна безпечно видалити |
| Плагіни або транспіляція ламають ESM | наприклад, Babel перетворює import/export на require |
Не увімкнено mode: 'production' у Webpack | у dev-режимі оптимізації вимкнені |
Приклад: Webpack
// webpack.config.js
module.exports = {
mode: 'production', // вмикає tree-shaking + minify
optimization: {
usedExports: true, // позначає використані експорти
}
};Якщо ти дивишся в bundle-analyzer, побачиш коментарі /* unused harmony export */ - це tree shaking у дії.
Приклад: Rollup (ідеальний випадок)
Rollup спочатку був побудований навколо tree shaking, тому він робить це найкраще:
// 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".
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.