Tree-shaking в Rollup основан на статическом анализе ES-модулей и
удалении неиспользуемого кода на этапе сборки. Однако эффективность
этого процесса напрямую зависит от того, насколько точно сборщик может
определить побочные эффекты выражений и конструкций. Одним из менее
очевидных, но критически важных параметров, влияющих на этот анализ,
является treeshake.tryCatchDeoptimization.
В основе оптимизации Rollup лежит анализ графа зависимостей и пометка кода как:
Конструкции try/catch попадают в последнюю категорию в
силу того, что Rollup не всегда может гарантировать отсутствие скрытых
побочных эффектов внутри try блока, особенно при
взаимодействии с импортируемыми модулями.
try {
someImportedFunction();
} catch (e) {}
В классическом сценарии статический анализ должен определить,
вызывает ли someImportedFunction побочные эффекты. Но если
функция импортирована из другого модуля, особенно из CommonJS или через
баррельные экспорты, Rollup может потерять точность анализа.
Любой try/catch блок влияет на оптимизацию по двум
причинам:
В результате Rollup применяет стратегию деоптимизации: он начинает
считать содержимое try потенциально опасным для
удаления.
Это приводит к тому, что даже «чистые» вызовы внутри try
могут быть сохранены в бандле, несмотря на отсутствие фактического
использования результата.
Настройка treeshake.tryCatchDeoptimization управляет
тем, насколько агрессивно Rollup будет снижать оптимизации внутри
try/catch конструкций.
По умолчанию параметр включён:
export default {
treeshake: {
tryCatchDeoptimization: true
}
};
При этом Rollup:
try блока потенциально имеющим
побочные эффектыtryПри включённой деоптимизации происходит расширение области «неопределённости»:
try чаще считаются
side-effectfulПример:
import { pureFn } from './utils.js';
try {
pureFn();
} catch (e) {}
Даже если pureFn является чистой функцией, Rollup может
сохранить вызов из-за невозможности гарантировать отсутствие побочных
эффектов.
Отключение параметра:
export default {
treeshake: {
tryCatchDeoptimization: false
}
};
меняет стратегию анализа. Rollup перестаёт автоматически считать
try блок зоной пониженной оптимизации.
Отключение приводит к более агрессивному tree-shaking:
try могут быть удаленыПример:
try {
pureUtility();
} catch {}
При tryCatchDeoptimization: false вызов может быть
полностью исключён, если pureUtility признана чистой.
Эффект tryCatchDeoptimization тесно связан с другими
механизмами Rollup:
package.json: sideEffectstreeshake.moduleSideEffectstreeshake.propertyReadSideEffectsЕсли модуль помечен как не имеющий побочных эффектов:
{
"sideEffects": false
}
то даже внутри try/catch Rollup может агрессивно удалять
код, особенно при отключённой деоптимизации.
Однако при включённой настройке tryCatchDeoptimization
эта оптимизация частично блокируется.
Особенно заметно влияние параметра в следующих случаях:
Многие полифилы используют try/catch для проверки
поддержки API:
try {
globalThis.crypto.randomUUID();
} catch {
fallback();
}
При включённой деоптимизации Rollup может сохранить весь блок, даже
если crypto.randomUUID доступен в окружении.
let init;
try {
init = require('./init.js');
} catch {}
Здесь require (особенно в CommonJS-interop) становится
причиной полной сохранности блока.
export function safeCall(fn) {
try {
return fn();
} catch {}
}
Даже при очевидной чистоте fn Rollup может не исключить
вызов внутри функции-обёртки.
Основная проблема, которую решает
tryCatchDeoptimization, заключается не в самом
try/catch, а в невозможности:
JavaScript не предоставляет информации о «типе чистоты» функций на уровне языка, поэтому Rollup вынужден опираться на эвристики.
При включённой деоптимизации:
При отключённой:
sideEffectsХотя параметр влияет в основном на результат, а не на скорость сборки, косвенные эффекты присутствуют:
В крупных проектах это может влиять на время финальной оптимизации и минификации.
Логика можно описать упрощённо:
try/catch → точный анализ чистотыtry/catch + деоптимизация → консервативная
модельtry/catch + отключённая деоптимизация → оптимистичная
модельЭта разница становится критичной в библиотеках, где активно используется функциональный стиль и высокая доля чистых функций.
treeshake.tryCatchDeoptimization не решает
фундаментальные проблемы:
Она лишь управляет уровнем консервативности анализа внутри одной синтаксической конструкции.
В сложных кодовых базах эффект этой настройки часто оказывается вторичным по сравнению с корректной архитектурой модулей и строгим определением side effects.