- PVSM.RU - https://www.pvsm.ru -
Это перевод статьи об оптимизации и уменьшении размера бандла приложения. Она хороша тем, что тут описаны best practices, советы, которых стоит придерживаться, чтобы тришейкинг работал и выкидывал неиспользуемый код из сборки. Она будет полезной многим, потому что сейчас все используют системы сборки, в которых «из коробки» есть тришейкинг. Но чтобы он работал правильно, нужно придерживаться принципов, описанных ниже.
Тришейкинг [1] становится основным приёмом, когда нужно уменьшить размер бандла и повысить производительность приложений на JS.
Как работает тришейкинг:
Файл с утилитами экспортирует две функции,
но используется только initializeName, formatName может быть удалена.
К сожалению, для правильной работы тришейкинга одной настройки сборщика недостаточно. Чтобы достичь лучшего результата, необходимо учесть множество деталей, а также удостовериться, что модули не были пропущены при оптимизации.
Существует огромное количество руководств по настройке тришейкинга. Начать погружение в тему лучше с официальной документации Webpack [1].
Стоит упомянуть, что несколько лет назад я создал бойлерплейт с уже настроенной сборкой и тришейкингом. Так что если вам нужна отправная точка для проекта, мой репозиторий может стать хорошим примером krakenjs/grumbler [2].
В этой статье рассматривается работа с Webpack, Babel и Terser. Тем не менее, большинство представленных принципов будут работать вне зависимости от того, используете ли вы Webpack, Rollup или что-то ещё.
Использование ES6 импортов и экспортов — первый и важнейший шаг к работающему тришейкингу.
Большинство других реализаций паттерна «модуль», включая commonjs и require.js, являются недетерминированными в процессе сборки. Эта особенность не позволяет сборщикам типа Webpack точно определить, что импортируется, что экспортируется и, как следствие, какой код может быть безболезненно удалён.
Варианты, возможные при использовании commonjs.
При использовании ES6 модулей возможности импорта и экспорта более ограничены:
ES6 модули имеют более простую семантику и правила использования.
Упрощённые правила позволяют сборщикам точно понимать, что было импортировано и экспортировано, и, как следствие, определять, какой код не используется вовсе.
Первая проблема, с которой вы можете столкнуться, используя Babel для транспиляции кода, — включённое по умолчанию преобразование ES6 модулей в commonjs. Это мешает сборщику оптимизировать код и выкидывать лишнее.
К счастью, в конфиге Babel существует простой способ отключить транспиляцию модулей [3].
После того как вы это сделаете, сборщик сможет взять транспиляцию импортов и экспортов на себя.
Webpack, как правило, оставляет экспорты нетронутыми в следующих случаях:
Такие экспорты будут либо полностью включаться в бандл, либо полностью удаляться. Значит, в итоге у вас может получиться бандл, содержащий код, который никогда не будет использоваться.
Обе функции будут включены в бандл, даже если используется только одна.
И здесь класс будет целиком добавлен в сборку.
Старайтесь сохранять ваши экспорты настолько маленькими и простыми, насколько это возможно.
В итоговый бандл попадёт только та функция, которая будет использована.
Выполнение этого совета позволяет сборщику выкидывать больше кода благодаря тому, что теперь в процессе сборки можно отследить, какая из функций была импортирована и использована, а какая не была.
Этот совет также помогает писать код в более функциональном и направленном на переиспользование стиле, а также избегать использования классов там, где это не оправдано.
Если вам интересно функциональное программирование, обратите внимание на эту статью [4].
При написании модулей многие люди упускают важный, но очень коварный фактор — влияние побочных эффектов.
Webpack не понимает, что делает window.memoize, и поэтому не может выкинуть эту функцию.
Заметьте, в примере выше window.memoize
будет вызвана в момент импорта модуля.
Как это видит Webpack:
window.memoize
, но я знаю, что она, возможно, вызовет add и создаст побочный эффект.В реальности мы уверены, что window.memoize
— чистая функция, которая не создаёт никаких побочных эффектов и вызывает add, если кто-то использует memoizedAdd
.
Но Webpack этого не знает и для спокойствия добавляет функцию add в итоговый бандл.
Для честности: последние версии Webpack и Terser необычайно хорошо справляются с выявлением побочных эффектов.
Даём Webpack больше информации и получаем оптимизированный бандл.
Теперь сборщику хватит информации для анализа:
memoize
на уровне модуля, это может быть чревато проблемами;memoize
пришла из ES6 импорта, нужно взглянуть на функцию в util.js
;add
, мы можем безопасно исключить её из итогового бандла.Когда Webpack не получает достаточно информации для принятия решения, он пойдёт по безопасному пути и оставит функцию.
Я нашёл два инструмента для выявления проблем.
Первый инструмент — module concatenation [5], плагин для Webpack, который позволяет добиться существенного прироста производительности. У него есть опция отладки. Стоит отметить, что факторы, предотвращающие конкатенацию и тришейкинг, одинаковые: например, побочные эффекты на уровне модуля. Воспринимайте предупреждения плагина серьёзно, так как любая проблема потенциально увеличивает размер бандла.
Второй — плагин для линтера https://www.npmjs.com/package/eslint-plugin-tree-shaking [6]. Я ещё не интегрировал его в свой бойлерплейт, потому что он не поддерживал flow, когда я экспериментировал с ним. Однако, он довольно хорошо определял проблемы с тришейкингом.
Старайтесь использовать оптимизированные для тришейкинга версии библиотек. Если вы импортируете большой бандл минимизированного кода, например jquery.min.js
, существует вероятность, что этот модуль не будет оптимизирован. Лучше поискать модуль, из которого можно импортировать атомарные функции, а для сборки и минификации использовать Webpack или Rollup.
Иногда вы можете импортировать всю библиотеку. Например, при использовании продакшен-билда React вам не нужно ничего выкидывать — всё, что в нём есть, уже оптимизировано.
Если вы используете библиотеку, экспортирующую отдельные функции, например lodash, попробуйте импортировать только нужные функции и обязательно удостоверьтесь, что остальные были исключены из итогового бандла.
У плагина DefinePlugin [7] для Webpack есть замечательная, но не самая известная фича — возможность влиять на то, какой код будет исключён в процессе сборки.
Если мы передадим __PRODUCTION__: true
в плагин, из итогового бандла будет исключён не только вызов функции validateOptions
, но и её определение.
Это упрощает создание разных бандлов для разработки и продакшена, а также помогает быть уверенным в том, что код, предназначенный для отладки, не попадёт в продакшен.
На глаз очень трудно определить, как Webpack будет оптимизировать конкретный модуль.
Так что запускайте билд, проверяйте итоговый бандл и смотрите, что получается. Посмотрите JavaScript-код и убедитесь, что в нём не осталось ничего лишнего, что должно было быть выброшено тришейкингом.
Если вы знаете другие полезные советы, напишите, пожалуйста, об этом в комментариях.
Автор: Дмитрий Татаринцев
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/347810
Ссылки в тексте:
[1] Тришейкинг: https://webpack.js.org/guides/tree-shaking/
[2] krakenjs/grumbler: https://github.com/krakenjs/grumbler
[3] отключить транспиляцию модулей: https://babeljs.io/docs/en/babel-preset-env#modules
[4] статью: https://medium.com/@bluepnume/functional-ish-javascript-205c05d0ed08
[5] module concatenation: https://webpack.js.org/plugins/module-concatenation-plugin/
[6] https://www.npmjs.com/package/eslint-plugin-tree-shaking: https://www.npmjs.com/package/eslint-plugin-tree-shaking
[7] DefinePlugin: https://webpack.js.org/plugins/define-plugin/
[8] Источник: https://habr.com/ru/post/490014/?utm_source=habrahabr&utm_medium=rss&utm_campaign=490014
Нажмите здесь для печати.