Как Размер Кода Зависит От Минификатора, Ассемблера И Языка. Неожиданное Обновление Веб-Пакета

Меня зовут Илья Гольдфарб, я разработчик интерфейса Яндекса.

Мне интересно наблюдать за тем, как развиваются инструменты для построения фронтендов, поэтому я стараюсь изучать изменения в каждом выпуске популярных решений.

В преддверии выхода пятой версии вебпака хочу рассказать о его, казалось бы, минорном релизе 4.26.0 от 19 ноября 2018 года, где неожиданно и без объявления войны изменилась дефолтная версия минификатора.

Раньше это был пакет UglifyJS, но теперь он использует Terser, ответвление UglifyES — ветки UglifyJS, которая может сжимать код как ES5, так и ES6. Терсер появился, когда главный сопровождающий отказался поддерживать и развивать UglifyES. Однако разработка UglifyJS также прекратилась с августа 2018 года, когда был выпущен последний выпуск.

В новом форке исправлены некоторые ошибки и немного переработан код. API этих минификаторов совместим, но они дают разные результаты сжатия.

Обычно изменения такого уровня происходят только в крупных, а не второстепенных обновлениях.

Из-за этого многие разработчики могут не обратить внимания на нововведение.

Конечно, в большинстве случаев все будет работать, но никто не хочет оказаться тем, у кого во время производства своего проекта возникают ошибки из-за системы сборки и минификации.

Вся эта история вдохновила меня на небольшое личное исследование сжатия.

Вот вопросы, которые я задал:

  • Что лучше сжимает ES5, Terser или UglifyJS?
  • Что загружается быстрее: сжатая версия ES5 от Terser или версия UglifyJS?
  • Какая версия весит больше: ES5 или ES6? И как TypeScript на это влияет?
  • Есть ли большая разница между настройками по умолчанию и настройками вручную?
  • А что, если это не вебпак? Кто производит меньшую сборку: Rollup или веб-пакет?
Для исследования я сделал немного приложение на React 16, который отображает приложение на Vue 2, который отображает приложение на Angular 7, у которого есть одна целая кнопка.

Всего было 3 529 695 байт неминифицированного кода (720 393 байта gzip).



Что лучше сжимает ES5, Terser или UglifyJS?

Я взял последнюю доступную версию UglifyJS, которая поставляется с веб-пакетом Terser с опцией ES5 и использовал те же настройки сжатия.

Размер в байтах Размер в байтах (gzip)
UglifyJS 1 050 376 285 290
Терсер 1 089 282 292 678
Итог: UglifyJS сжимает на 3,5% лучше (2,5% gzip).



Что загружается быстрее: сжатая версия ES5 от Terser или версия UglifyJS?

Производительность я измерял с помощью стандартных DevTools Яндекс Браузера.

Я загрузил страницу 12 раз и взял значение Scripting (время выполнения скрипта), отбросив первые три измерения.

UglifyJS — 221 мс (ошибка 2,8%).

Терсер - 226 мс (погрешность 2,7%).

Итог: значения слишком малы для такой ошибки; их можно считать одинаковыми.

Мы также пришли к выводу, что этот метод не подходит для измерения времени загрузки.

Скорость кода я не измерял и не сравнивал, так как разные коды работают по-разному.

Разработчики каждого проекта должны самостоятельно разобраться в этом вопросе.



Какая версия весит больше: ES6 или ES5? И как TypeScript на это влияет?

Чтобы сравнить две версии и сосредоточиться исключительно на технологии, я взял Плагины Babel и сделал четыре сборки:
  • ES5: все плагины с пометкой es2016 + плагин для Object.assign + более поздние плагины + экспериментальные плагины, в качестве цели в tsconfig установлено значение ES5;
  • ES5 (ts esnext): все плагины с пометкой es2016 + плагин для Object.assign + все более поздние плагины + экспериментальные плагины, в качестве цели в tsconfig установлено значение esnext;
  • ES6: только плагины для es2017 и более поздних версий + экспериментальные плагины, в tsconfig для цели установлено значение ES6;
  • ES6 (ts esnext): только плагины для es2017 и более поздних версий + экспериментальные плагины, в tsconfig для цели установлено значение esnext.
Размер в байтах Размер в байтах (gzip)
ES5 1 186 520 322 071
ES5 (тс эснекст) 1 089 282 292 678
ES6 1 087 220 292 232
ES6 (тс эснекст) 1 087 220 292 232
Итог: версия, сжатая Babel с машинописной компиляцией для esnext, весит на 97 238 байт (8,2%) меньше.

Так неожиданно получилось, потому что Angular написан на TypeScript, а Vue и React на JavaScript. Terser, как и Uglify, при сборке с вебпаком не может вырезать неиспользуемый кусок кода, поставляемый Typescript из Angular. Это ошибка компиляции для этого примера.

В сборке на другом проекте его может не быть и разница будет гораздо меньше.

Вы также можете видеть, что объем кода ES6 всего на 2062 байта меньше, чем у ES5. На любимом проекте я получил совсем другой результат: кода ES6 на 3-6% больше, чем ES5. Это объясняется несколькими факторами, двумя основными из которых являются: 1. Помощник Babel для наследования классов вставляется один раз и далее стоит четыре байта (e(a,b)), а в ES6 используется нативное наследование стоимостью 15 байт (класс a расширяет b).

2. Метод объявления переменных.

В ES5 это переменные, и они отлично сжимаются.

Но в ES6 это let и const, которые сохраняют порядок инициализации и не объединяются друг с другом.

Небезопасная агрессивная минификация, такая как принудительное использование стрелочных функций или использование свободной настройки, поможет уменьшить размер кода ES6. Будьте внимательны и учитывайте тонкости.

Например, в Firefox стрелка функционирует в в четыре раза медленнее , чем обычно, но в Хроме разницы нет. Поэтому однозначно ответить на вопрос невозможно: результат сильно зависит от кода и целевой среды выполнения.



Есть ли большая разница между настройками по умолчанию и настройками вручную?

Давайте сравним, можно ли получить меньший размер файла, если немного подправить настройки.

Например, укажем, что минификацию необходимо повторить пять раз.

По умолчанию он запускается только один раз.

Размер в байтах Размер в байтах (gzip)
Терсер (по умолчанию) ES5 1 097 141 294 306
Терсер (проходит 5) ES5 1 089 312 292 408
Уродовать (по умолчанию) ES5 1 091 350 294 845
Уродство (проходит 5) ES5 1 050 363 284 618
Итог: Uglify с пятикратным минимизацией на 3,7% меньше, чем Uglify по умолчанию (3,4% gzip).

Поэтому всегда необходимо корректировать настройки сжатия.

Кстати, пятикратная минификация не означает, что сборка займет в пять раз больше времени.

Например, в этом тестовом проекте однократная минификация занимает 18 секунд, пятикратная минификация — 38, а десятикратная — 49. Рекомендую экспериментальным путем найти идеальное значение для вашего проекта, после чего минификация завершится.

остановитесь, и код не изменится.

Обычно это от 5 до 10. Есть еще куча других вариантов: comment:false вырезает все комментарии о лицензиях (хотя это юридический вопрос), а hoist_funs:true группирует функции в одном месте, что позволяет лучше оптимизировать переменные.

В идеале нужно пройти все настройки .



Кто производит меньшую сборку: Rollup или веб-пакет?

Свернуть — альтернативный коллектор со встроенным механизмом встряхивания дерева.

Для теста я сделал сборку на Rollup 0.67.4 с теми же настройками, что и у webpack.

Размер в байтах Размер в байтах (gzip)
Накопительный пакет ES5 (Uglify) 990 497 274 105
Накопительный пакет ES5 (Терсер) 995 318 272 532
веб-пакет ES5 (Uglify) 1 050 363 284 618
веб-пакет ES5 (Терсер) 1 089 312 292 408
Результат: результат от Rollup и Uglify на 5,6% (3,6% gzip) меньше.

Это произошло по нескольким причинам: 1. Webpack содержит костыли для крайних случаев.

Например, этот код оборачивает каждый вызов функции из другого модуля в Object().

Это сделано для того, чтобы контекст модулей без использования strict не передавался в модули с use strict. Хорошо написанные проекты без сторонних зависимостей не нуждаются в обертке, но иногда в сборке задействовано нечто большее, чем просто хорошо написанный код. И в этом плане вебпак выглядит надежнее.

Rollup, в свою очередь, считает, что все модули — это ES6-модули, и они всегда исполняются в use strict, поэтому для него этой проблемы просто не существует. Важный вопрос — как такие костыли вебпаков влияют на производительность.

Давайте представим, что мы написали идеальный код, который не нуждается в дополнительных обертках, но все равно каждый вызов функции будет проходить через них.

Это добавляет небольшие накладные расходы на выполнение: примерно одну сотую микросекунды на вызов функции в Chromium (одну десятую в Firefox).

2. В вебпаке есть небольшой бутстрап, управляющий инициализацией и загрузкой модулей.

Rollup не использует обертки, а просто сбрасывает код всех модулей в одну область видимости.

В Webpack есть аналогичная оптимизация, но она работает не со всеми модулями.



Результаты исследования

Я надеюсь, что после прочтения этой статьи многие проверят свои системы сборки и удостоверятся, что они используют все возможные методы для наилучшего сжатия.

Это быстро и легко.

Сначала правильно настройте комбинацию TypeScript и Babel. Пусть каждый компонент сборки занимается своим делом: один проверяет типы, а второй отвечает за преобразование в устаревшие стандарты.

Во-вторых, при использовании ES5 вы можете изменить минификатор обратно на UglifyJS, но помните, что он больше не поддерживается.

В-третьих, для сборки предпочтительнее выбирать Rollup. Однако это возможно не во всех случаях из-за отсутствия некоторых плагинов.

После сборки не забудьте проверить работоспособность функциональными тестами.

Если у вас их нет, самое время начать их писать.

В опросе могут участвовать только зарегистрированные пользователи.

Войти , Пожалуйста.

Как вы собираете свои проекты 9,09% накопительный пакет 28 84,74% веб-пакет 261 18,18% gulp 56 3,25% хрюканье 10 3,9% самописное решение 12 Проголосовали 308 пользователей.

73 пользователя воздержались.

Теги: #Исследования и прогнозы в ИТ #Разработка сайтов #JavaScript #оптимизация #webpack #Сжатие данных #сборка #роллап

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.