Как min js файлы сделать читаемыми
В первой части этой статьи я говорил о том, что такое модули, почему разработчики их используют, а также рассказал о различных способах подключения модулей в свой код.
Во второй части я более подробно расскажу о том, что означает "сборка" модулей, почему мы собираем модули, различные способы сделать это, и какое место занимают модули в будущем веб-разработки.
Что такое сборка модулей?
Сборка модулей - это просто процесс склеивания группы модулей (и их зависимостей) в один файл (или группу файлов) в правильном порядке.
Как и со всеми аспектами веб-разработки, все неприятности скрываются в деталях. :)
Зачем вообще собирать модули?
Когда вы разбиваете вашу программу на модули, как правило вы организовываете их так, чтобы они находились в разных файлах и папках. Скорее всего, у вас также будет группа модулей для библиотек, которые вы используете, таких как Underscore или React.
В результате, каждый из этих файлов должен быть подключён в HTML-файл в виде тега <script>, который загружается в браузер при посещении пользователем страницы. Если для каждого файла будет отдельный тег <script>, это означает, что браузер должен загружать каждый файл по отдельности, один за другим. Это плохо сказывается на времени загрузки страницы.
Для того чтобы избавиться от этой проблемы, мы собираем или "объединяем" все наши файлы в один большой (или парочку больших, в зависимости от обстоятельств), тем самым уменьшая количество запросов к серверу. Когда вы слышите , что разработчики говорят "этап сборки" или "процесс сборки", он имеют ввиду то, о чём мы только что говорили.
Ещё один распространённый подход для ускорения операций сборки, это "минификация" собираемого кода. Минификация - это процесс удаления ненужных символов из исходного кода (таких как пробелы, комментарии, символы новой строки и т.д.) в целях уменьшения общего размера содержимого, без изменения функциональных свойств кода.
Меньше данных означает, что браузеру потребуется меньше времени для загрузки и обработки файлов. Если вы когда-нибудь видели файл с префиксом min такой как underscore-min.js, то наверняка заметили, что он крошечный (и не читаемый) по сравнению с полной версией.
Менеджеры задач такие как Gulp и Grunt выполняют объединение и минимизацию файлов очевидным для разработчиков способом и гарантируют, что человеко-понятный код останется для разработчиков, в то время как оптимизированный код в собранном виде получат браузеры.
Какие способы для сборки модулей существуют?
Объединение и минимизация ваших файлов прекрасно работает если вы используете один из стандартных паттернов (обсуждали в прошлой статье). Но по факту всё что вы делаете, дак это всего навсего перезаписываете кучу нативного JavaScript кода.
Тем не менее, если вы придерживаетесь не нативных систем модулей, которые не могут интерпретироваться браузерами, например, CommonJS или AMD (или даже нативный ES6), то вам необходимо использовать специальный инструмент для преобразования модулей в упорядоченный и понятый браузеру код. Вот здесь то и вступают в игру Browserify, RequireJS, Webpack и другие "сборщики модулей" или "загрузчики модулей".
В дополнении к сборке и/или загрузке ваших модулей, сборщики модулей предлагают массу дополнительных возможностей, например, авто-перекомпиляция кода при внесении изменений.
Давайте рассмотрим некоторые общие методы сборки.
Сборка CommonJS
Как вы уже знаете из первой части статьи, CommonJS загружает модули синхронно, и это хорошо, но не целесообразно для браузеров. Также я отметил, что есть обходные решения для этой проблемы, один из них сборщик модулей Browserify. Browserify - это инструмент, который компилирует модули CommonJS для браузеров.
Предположим, что у вас есть файл main.js, который импортирует модуль для вычисления среднего значения в массиве чисел.
В этом случае у нас есть одна зависимость (myDependency). С помощью команды представленной ниже, Browserify рекурсивно собирает все зависимые модули, начиная с main.js, в один один файл названный bundle.js.
Таким же образом, если у вас есть несколько файлов, с несколькими зависимостями, просто скажите Browerify, что именно нужно записать в файл и сидите сложа руки, пока он делает свою магию.
Итоговый файл готов для таких инструментов как Minify-JS, чтобы минимизировать собранный код.
Сборка AMD
Если вы используете AMD, наверняка вы захотите использовать AMD загрузчик, такой как RequireJS или Curl. Загрузчик модулей (в отличии от сборщика) динамически загружает модули, которые необходимы для запуска вашей программы.
Напомню, что одним из основных отличий AMD от CommonJS является то, что модули загружаются асинхронно.
С AMD вам технически не нужен этап сборки, на котором вы объединяете все модули в один файл. Асинхронная загрузка модулей означает то, что вы постепенно загружаете модули по мере необходимости, вместо загрузки сразу всех файлов при первом посещении страницы пользователем.
В реальности однако, большой объём запросов на каждое действие пользователя не подходит для продакшена. Большинство разработчиков, до сих пор используют инструменты для сборки и минификации кода (такие как RequireJS optimizer, например), чтобы достичь оптимальной производительности.
В целом, когда дело доходит до сборки, разница между AMD и CommonJS заключается в следующем: при разработке AMD приложений вы можете пропускать этап сборки. По крайней мере пока вы пишите код, после этого оптимизаторы такие как r.js могут заняться этим делом.
Если хотите интересную дискуссию на тему AMD vs CommonJS, можете прочитать статью в блоге Tom Dale’s :)
Webpack
До сих к нам приходят сборщики модулей, Webpack - это ещё совсем ребёнок в этой области. Он был разработан для того, чтобы стать агностиком к системе модулей, которую вы используете, позволяя использовать CommonJS, AMD или ES6 в зависимости от обстоятельств.
Вы можете удивиться, зачем нам нужен Webpack, если уже есть такие сборщики как Browserify или RequireJS, которые делают свою работу хорошо. В Webpack есть некоторые полезные функции, например, такие как "разделение кода (code splitting)" - способ разделить код на "куски (chunks)" и загружать их при необходимости.
Для примера, если в вашем веб-приложении есть блоки кода, которые необходимо загружать только при определённых обстоятельствах, будет неэффективно собирать всё в один большой файл. В этом случае, вы можете использовать "code splitting" для извлечения кода в собранные куски, которые могут быть загружены при необходимости, во избежании неприятностей, когда большинству пользователей нужно только ядро приложения.
Разделение кода - это только одна из множества функций, которые предлагает Webpack, а в интернете полно споров о том что лучше, Webpack или Browserify. Вот несколько уравновешенных дискуссий, которые мне показались полезными в этом вопросе:
Модули ES6
Уже вернулся? Хорошо, потому что я хочу поговорить о ES6 модулях, которые в будущем, в некоторой степени могут снизить потребность в сборщиках (вы поймёте о чём я). Во-первых давайте разберёмся как загружаются модули в ES6.
Самое важное отличие между текущими JS модулями (CommonJS и AMD) и ES6 модулями заключается в том, что в ES6 модулях из коробки есть статический анализ. Это означает, что импорт выполняется ещё до компиляции. Это позволяет удалить экспорты, которые не используются другими модулями перед запуском программы. Удаление неиспользуемых экспортов позволяет значительно сэкономить пространство и уменьшить нагрузку на браузер.
Один вопрос, который возникает у всех: В чём разница между подходом описанным выше и подходом связанным с удалением лишнего когда с помощью плагинов типа UglifyJS. Ответ как всегда прост "это зависимость".
Иногда, удаление неиспользуемого кода может работать одинаково как UglifyJS так и в ES6 модулях, а иногда нет. Есть очень крутой пример Rollup’s wiki, можете посмотреть если есть желание.
Отличительной чертой ES6 модулей, является другой подход к устранению неиспользуемого кода, который называется "встряхивание дерева (tree shaking)". Встряхивание дерева - это по сути процедура обратная удалению неиспользуемого кода. Она не удаляет неиспользуемый код, а включает только необходимый для работы код. Давайте рассмотрим на примере. Допустим, у вас есть файл utils.js с функциями, каждую из которым мы экспортируем используя синтаксис ES6.
Дальше, давайте предположим, что мы не знаем как именно функции из utils.js мы будем использовать, поэтому идём дальше и импортируем все модули в main.js вот так.
В итоге, позже мы стали использовать только одну функцию each.
Версия нашего main.js файла после загрузки модулей подходом "tree shaken", будет выглядеть следующим образом.
Обратите внимание на то, что экспортировалась только задействованная функция each.
В то же время, если мы решим использовать функцию filter вместо функции each, и напишем что-нибудь типа:
Версия после "tree shaken", будет выглядеть так:
Обратите внимание на то, что в этот раз включены обе функции each и filter. Это произошло потому что filter использует each, поэтому нам необходимо экспортировать для работы модуля.
Довольно ловко, неправда ли?
Я призываю вас, чтобы вы поигрались и исследовали "tree shaken" в онлайн редакторе Rollup.js.
Разработка ES6 модулей
Итак, мы знаем, что ES6 модули загружаются не так как другие форматы модулей, но до сих пор не говорили об этапе разработки ES6 модулей.
К сожалению, ES6 модули требуют дополнительной работы, так как являются не родными, для реализованной загрузки модулей в бразуерах, пока что.
Вот несколько вариантов разработки/конвертирования ES6 модулей в формат привычный для браузеров. №1 на сегодня является наиболее распространённым подходом.
- Используйте "транспилер (компилятор типа исходный код в исходный код)" (например, Babel или Traceur) чтобы преобразовать ваш ES6 код в ES5 код любого формата CommonJS, AMD или UMD. Затем пропустите скомпилированный код через сборщик такой как Browerify или Webpack, чтобы создать один или несколько собранных файлов.
- Использование Rollup.js, который похож на первый вариант, за исключением того, что накладывает мощь ES6 модулей для статического анализа вашего ES6 кода и обработки зависимостей перед сборкой. Он использует "tree shaking" чтобы включить только необходимый минимум в вашу сборку. В целом, основное преимущество перед Browserify или Webpack, когда вы используете ES6 и заключается в том, что "tree shaking" может сделать ваши сборки меньше. Проблема заключается в том, что Rollup предлагает несколько форматов для ваших пакетов, включая ES6, CommonJS, AMD, UMD или LIFE. LIFE и UMD сборки будут работать в вашем браузере, но если вы выберете AMD, CommonJS или ES6, то вам нужно найти другие способы конвертировать этот код в форма понятный для браузера (например, с помощью Browserify, Webpack, RequireJS и т.д.).
Прыжки через обручи
Как веб-разработчики, мы должны прыгать через большое количество обручей. Не всегда легко превратить наши красивые ES6 модули во что-то, что браузеры смогут интерпретировать.
Вопрос в том, когда ES6 модули будут запускаться в браузере без всяких проблем?
Ответ на этот вопрос к счастью "рано или поздно".
На текущий момент, в ECMAScript есть решение, которое называется ECMAScript 6 module loader API. Короче говоря, это программное Promise-based API, которое должно динамически загружать модули и кэшировать их так, чтобы при последующем импорте не перезагружать новую версию этого модуля.
Примерно, это будет выглядеть так:
myModule.js
main.js
Альтернативный способ определить модуль, это указать напрямую атрибут type="module" непосредственно в теге script, вот так:
Если вы ещё не смотрели репозиторий полифилла для API загрузчика модулей, то я настоятельно рекомендуют вам, хотя бы заглянуть в него.
Кроме того, если вы хотите устроить тест-драйв, посмотрите SystemJS, который разработан на основе полифилла загрузчика модулей ES6. SystemJS динамически загружает модули любых форматов (ES6, AMD, CommonJS и/или глобальные скрипты) в браузер. Также он отслеживает все загруженные модули через "реестр модулей", для предотвращения повторной загрузки. Не говоря уже о том, что он автоматически компилирует ES6 модули (если установить нужный параметр) и позволяет комбинировать различные типы модулей. Довольно круто.
Будут ли нужны нам сборщики модулей, если у нас теперь есть нативные ES6 модули?
Растущая популярность ES6 модулей приводит нас к некоторым интересным последствиям:
Устареют ли CommonJS, AMD и UMD?
После того как ES6 станет стандартом для модулей, нужны ли нам будут другие форматы?
Я сомневаюсь в этом.
Веб-разработка значительно выигрывает от единого стандартизированного подхода для импорта и экспорта модулей в JavaScript. Сколько времени нужно, чтобы ES6 стал стандартом для модулей?
Скорей всего, довольно долго)
Плюс ко всему, есть много людей, которым нравятся "разновидности". Так что "единственно правильный подход" может никогда не стать реальностью.
Вывод
Надеюсь, что две эти статьи помогли вам разобраться в терминологии модулей. Проверьте первую часть, если какие-то из вышеуказанных терминов сбивают вас с толку.
Я подробно рассмотрел на примере популярных CMS, как выполнить минификацию (сжатие) исходного кода сайта. Для кого-то это может показаться совсем не нужным дополнением, но я рекомендую уделить этому особое внимание.
Для чего вообще делается сжатие файлов? Для того, чтобы уменьшить их вес, тем самым ускорить работу вашего сайта.
Приведу простой пример. У вас есть не сжатая библиотека jQuery, которая весит 120 килобайт. А в сжатом виде эта же библиотека будет весить 60 килобайт. Получаем экономию в 60 КБ, что вполне неплохо. А если таких файлов 15-20?
Вообще сжатие файлов можно производить и вручную через какой-нибудь онлайн-сервис, а можно поступить куда умнее и воспользоваться готовым PHP-скриптом, с помощью которого легко можно решить поставленные задачи.
Минификация (сжатие) CSS-файлов на PHP
Основная идея минификации стилей будет заключаться в следующем: мы объединяем все файлы в один и удаляем в нем все переводы строк, символы табуляции, двойные пробелы и комментарии.
PHP-скрипт будет выглядеть следующим образом:
В нем вы прописываете адреса до всех ваших файлов стилей и отправляете итоговый файл на сайт по FTP или другим удобным для вас способом.
После чего на сайте вы удаляете указанные в скрипте стили и подключаете новый стиль по такому принципу:
Где «/style.php» – это ссылка до ранее созданного PHP-файла.
Сохраняете все изменения и смотрите, не поехал ли дизайн на вашем сайте, если поехал – то проверьте, корректные ли адреса вы прописали в скрипте.
Теперь, если вы обратитесь к вашему скрипту через браузер, то увидите, что ваши стили максимально оптимизированы.
Минификация (сжатие) JS-файлов на PHP
Оптимизация скриптов выполняется примерно по тому же принципу: объединяем все файлы в один и удаляем в нем переводы строк, двойной пробел и знак табуляции, а также комментарии кода.
Итоговый скрипт будет выглядеть следующим образом:
Не забывайте в нем прописать ссылки до ваших скриптов.
И по аналогии с CSS-файлами – подключение следующее:
Где «/script.php» – ссылка до ранее созданного PHP-скрипта.
На что здесь стоит обратить внимание?
- Проставляйте корректные ссылки до ваших файлов.
- На вашем хостинге должна быть поддержка PHP.
В остальном вроде проблем быть не должно, но если все-таки они появились – пишите об этом в комментариях, постараюсь вам помочь.
В процессе оптимизации каждый веб-разработчик приходит к необходимости сжимать CSS и JavaScript. Обычно это делается путем незначительных изменений в коде + удаления пробелов и отступов. Читаемость кода от этого сильно страдает, но не функциональность. Пользователь выигрывает в трафике, вебмастер — в скорости загрузки.
Также часто возникает задача наоборот — развернуть сжатый JavaScript в читаемое состояние, чтобы внести правки (если оригинал потерялся) или прочитать код конкурентов:)
Сжатие JavaScript
Часто для этого используют YUI. Это очень популярный инструмент, который может сжимать не только JavaScript, но и CSS. Воспользоваться этим инструментом онлайн можно только на сторонних сайтах. Попробуйте этот. Выставьте настройки и вуаля! Ваш новый код. Не узнаете? Так и задумывалось. Можно использовать его на ваших страницах. Он будет работать так же хорошо, как и оригинал, только весит в два раза меньше. Я тестировал сжатие на скрипте, который весит 50,1 килобайт. YUI сжал его до 31,9 кб. Еще есть два алгоритма сжатия JavaScript — Minify (JSmin) и Packer (Dean Edwards). Первый на том же исходном файле дал мне 32 килобайта, второй — 29,3! Лучший результат. Не факт что на всех скриптах результат будет таким же и я бы советовал пробовать сжатие всеми алгоритмами и выбирать лучший для каждого скрипта.
Разворачивание и чистка JavaScript
Чтобы преобразовать сжатый JavaScript в читаемый вид, нужно воспользоваться онлайн-инструментами. Их в сети много, но все они мне не нравятся кроме одного. Beautify Javascript. У него есть свой сайт, где можно скачать плагины для браузеров и текстовых редакторов и использовать его непосредственно. Очень приятно все это сделано и форматирует — прямо загляденье! Всем рекомендую. А если у вас телефон самсунг s5230 — предлагаю вам скачать темы для самсунга s5230 бесплатно;)
Одним из пунктов оптимизации сайта является минимизация кода, который передается браузеру посетителя в момент открытия сайта. Сюда относится как содержимое CSS-файлов и JS-файлов, так и HTML-код страниц. Данные меры позволяют заметно сократить размер итогового кода и немного ускорить загрузку сайта.
Минимизация происходит за счет удаления лишних пробелов, табуляции и пустых строк. Расскажу, каким образом я делаю это на своих сайтах.
Минимизация HTML-кода
Реализуется это с помощью двух небольших вставок PHP-кода, в котором используются регулярные выражения.
Первую часть необходимо вставить в самое начало исходного кода вашего сайта (т.е. прямо перед <!DOCTYPE . > ):
А вторую часть, напротив, необходимо вставить в самый конец исходного кода сайта, т.е. после тега </html> :
При этом содержимое тега pre добавлено в исключение, т.е. не минимизируется, это необходимо для правильного отображения примеров кода.
Буду рад, если кто-то в комментариях подскажет, как грамотно устранить данный недостаток, не ломая при этом работоспособность скриптов.
Минимизация CSS-файлов и JS-файлов
Для этого я использую замечательный бесплатный инструмент Minify. Это PHP-приложение, которое кладется в отдельную папочку на сайте и через которое пропускаются все необходимые файлы.
Можно минимизировать как отдельные файлы, так и сгруппировать несколько файлов в один, тем самым сократив количество запросов к серверу.
Подключается он просто:
Если у вас несколько файлов одного типа, то рекомендую воспользоваться группировкой (для этого редактируется файл /min/groupsConfig.php ). В билдере, да и в самом этом файле показаны примеры, как объединить несколько CSS- или JS-файлов.
Кстати, даже если у вас всего один файл, и вы хотите при минимизации сократить путь до него, тогда тоже можно использовать группировку.
К примеру, на этом блоге я сделал так:
На этом все, спасибо за внимание. Надеюсь, эта информация окажется для вас полезной.
Читайте также: