Почему не работает Tree Shaking и как с этим жить

в 11:34, , рубрики: javascript, rollup, voximplant, webpack, Блог компании Voximplant, Программирование, Разработка веб-сайтов, разработка мобильных приложений
Почему не работает Tree Shaking и как с этим жить - 1

В нашей предыдущей статье про голосовых ботов для Рокетбанка читатели возмутились, что в 2017 году примеры JavaScript для облака Voximplant написаны на ES5. У нас в облаке сильно модифицированный SpiderMonkey, специально обученный не течь и не падать. Тысячи одновременных звонков с параллельно выполняемым JavaScript как бы намекают, что нода – для нас не вариант. Тем не менее, никто не мешает использовать транспайлеры, компилировать ES2017/TypeScript/Elm/Whatever в старый добрый JavaScript и загружать результаты компиляции с помощью Continuous Integration. При таком раскладе возникает соблазн использовать все последние достижения из npmjs, собирая весь код в один ES5 бандл. И вот тут нас ждет засада: даже один метод из lodash дает на выходе бандл размером в полмегабайта. И не похоже, чтобы рекламируемый последние пару лет tree shaking работал.

Кто трясет деревья?

Огромные JavaScript бандлы — это не очень хорошо. В мире браузеров они увеличивают время загрузки страницы: сначала такой бандл надо скачать, потом распарсить, потом выполнить. В мире backend и скриптов-в-облаке тоже свои нюансы. Чтобы выполнять тысячи звонков в секунду, контролируемых через JavaScript, наш SpiderMonkey ограничивает память JavaScript сессии 16-ю мегабайтами. Это на все: исходный код, ast, структуры данных. Архитектура нашей платформы подразумевает, что в облаке выполняется код, который должен работать в реальном времени. А все «тяжелые» вещи можно перенести на свой backend и делать к нему HTTP запросы прямо во время звонка. Беда в том, что пара методов из lodash выглядят как замечательная идея для легковесного кода в облаке. Хоп — и плюс полмегабайта к результирующему JavaScript.

Сообщество JavaScript разработчиков знает об этой проблеме давно, и кроме «давайте выкинем все пробелы и переименуем что не страшно в однобуквенные варианты» (uglify до dead code elimination) активно разрабатывает «Tree Shaking». В идеале, «Tree Shaking» должно убирать весь неиспользуемый код: импорты, вызовы методов, глобальные переменные. И для нашего кода мы должны получить несколько функций lodash, наш код – и всё. А вместо этого получаем lodash целиком. WTF?

Webpack, Rollup и Uglify

Поддержка tree shaking считается сильной стороной rollup, заявлена в последних версиях webpack и уже давно присутствует в UglifyJS в виде «dead code elimination». О разнице между «dead code elimination» два года назад очень хорошо написал автор Rollup: если dead code elimination получает на вход скомпилированный бандл и пытается выкинуть из него неиспользуемый код, то tree shaking работает с AST кода во время компиляции и пытается включить только тот код, который используется. Кстати, Webpack рассчитан на комбинированный подход: вначале tree shaking во время сборки бандла, а затем dead code elimination с помощью UglifyJS плагина.

Только в реальном мире Tree Shaking не работает.

По словам самих авторов, определить используемый код в слабо типизированным языке – задача нетривиальная. И, чтобы ничего не сломать, в непонятных ситуациях код всегда включается. К несчастью, примерами таких «непонятных» ситуаций являются самые популярные библиотеки общего назначения: lodash, underscore, – все вот эти ребята.

Что делать?

Можно, конечно, подождать еще пару лет. Вывод типов становится лучше, ведутся работы над поддержкой tree shaking для типизированных диалектов вроде TypeScript. Но писать ES2017 код с библиотеками хочется сейчас. Без многомегабайтных бандлов.

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

Конечно, это демонстрация «в лоб» с пустыми конфигами webpack/rollup. Можно докрутить и до более впечатляющих цифр, но основная идея в том, что не стоит огорчаться тысячам зависимостей, которые ставит yarn. Минимально допиленный напильником стек позволяет выкинуть большую часть неиспользуемого кода и получить вполне читаемый бандл для загрузки в Voximplant или любую другую платформу, которые программируется на JavaScript.

Картинка до ката из статьи про tree shaking в webpack 2

Автор: eyeofhell

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js