Grunt - что это такое и как с ним работать

Оригинал — статья Grunt for People Who Think Things Like Grunt are Weird and Hard

Фронтэнд разрабочикам часто говорят:

Разрабатывайте всё в маленьких кусках CSS и JavaScript кода, чтобы вам было понятно, а дальше соберите их вместе для продакшн сайта. Сожмите ваш CSS и минифицируйте JavaScript, чтобы размеры файлов были минимальными в работающем сайте. Оптимизируйте ваши изображения для уменьшения их размера без ущерба для качества. Используйте Sass для создания CSS из-за возможности использования многочисленных полезных абстракций. Это не всеобъемлющий список действий, но некоторые из них мы должны делать. Вы можете называть их задания. Могу поспорить, что вы уже слышали о Grunt. Короче говоря, Grunt — это исполнитель заданий. Grunt может делать их за вас. После того, как вы его установили, что на самом-то деле и не особенно сложно, то он выполняет автоматически, без необходимости снова продумывать все задания. Но давайте начистоту: Grunt — это одна из новомодных штукенций, которой пользуются все крутые детишечки, но которая на первый взгляд кажется странной и недоступной. Я вас понимаю. Эта статья именно для вас.

Давайте разберёмся со всеми вами

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

Мне не нужно всё то, что делает Grunt

На самом деле совсем наоборот. Просмотрите ещё раз список чуть выше. Эти все вещи совсем неприятно делать. Но сегодня — это всё входит в цикл разработки сайта. Если вы уже делаете их все, то это просто отлично. Может вы используете множество разных инструментов, чтобы всё выполнить. Grunt поможет, так сказать, поместить их все под одну крышу. Если же вы ещё не делаете, тогда мы идём к вам Grunt поможет вам. После того, как вы начнёте использовать Grunt, то он может делать многие другие вещи, что вам позволит выполнять лучше вашу работу.

Grunt работает на Node.js — я совсем не знаю Node

Вам не нужно знать Node. Совсем как вам не нужно знать Ruby, чтобы использовать Sass. Или PHP, чтобы использовать WordPress. Или C++, чтобы использовать Microsoft Word.

У меня есть куча других способов сделать то, что может сделать для меня Grunt

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

Grunt это командная строка — а я же просто дизайнер

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

Максимум, когда нужно использовать командную строку:

Перейти в директорию проекта. Напечатать grunt и нажать Enter. После установка, что опять же не очень сложно.

Ну ладно. Давайте уже поставим этот GRUNT

Условием для работы Grunt является предустановленная Node. Если Node у вас не установлена, то не волнуйтесь — это очень просто сделать. Вам просто нужно скачать установщик и запустить его — официальный сайт Node.

Ставить Grunt нужно для каждого проекта. Перейдите в папку проекта. Нужно создать файл с названием package.json в корне:

Создание конфигурационного Grunt файла

Со следующим содержимым:

{
  "name": "example-project",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.1"
  }
}

Можете спокойно менять название проекта и версию, но все devDependencies вещи должны оставаться такие как сейчас.

Это то, как Node работает с зависимостями. У Node есть менеджер пакетов, который называется NPM (Node packaged modules) для управления Node зависимостями (например, как gem для Ruby, если вам такое знакомо). Вы даже можете думать об этом как о плагине для WordPress.

После создания package.json в корне, перейдите в терминал и пройдите в вашу папку. Терминальные ламеры как я, например, делают это так:

Смена директории в терминале для чайников

И дальше запустил команду:

npm install

После запуска этой команды, появится новая папка в вашем проекте с названием node_modules.

Пример папки node_modules

Другие файлы, что вы видите, README.md и LICENSE здесь потому что я собираюсь положить этот проект на GitHub и это стандарт в таких случаях.

Последний шаг в инсталяции — это установить Grunt CLI (command line interface — интерфейс командной строки). Это то, что даёт возможность запускать grunt команды из строки. Без этого, любые grunt команды просто вызовут ошибку «Command Not Found». Эта отдельная установка нужна для эфективности. Иначе бы у вас было 10 проектов с 10 копиями Grunt CLI.

Дальше запустите следующую команду в терминале:

npm install -g grunt-cli

Дальше закройте и откройте терминал. Это один из способов проверить правильно ли всё работает. Схоже с перезапуском компьютера в старые добрые времена, как при установке какой-либо программы.

Давайте соединим с помощью GRUNT некоторые файлы

Допустим в нашем проекте есть три отдельных JavaScript файла:

  • jquery.js — используемая нами библиотека.
  • carousel.js — jQuery плагин.
  • global.js — Пользовательский JavaScript файл, где мы всё конфигурируем и вызываем наш плагин.

В продакшене, мы могли бы объединить все эти файлы вместе из соображений производительности (один запрос лучше, чем три). Там нужно скомандовать Grunt сделать это.

Но постойте. Grunt вообще-то не делает всё сам по себе. Это исполнитель задач. Задачи естественно нужно ещё добавить. Мы ещё не настраивали Grunt для какой-то работы, поэтому давайте сделаем это.

Официальный Grunt плагин для объединения файлов — это grunt-contrib-concat. Вы можете прочитать о нём на Гитхабе, но для работы всё что нужно — это запустить следующую команду из строки (естественно в корне вашего проекта):

npm install grunt-contrib-concat --save-dev

Толково будет сделать это следующим образом: ваш package.json файл будет автообновится, чтобы включить эту новую зависимость. Откройте и проверьте файл. Вы увидите новую строку:

"grunt-contrib-concat": "~0.3.0"

Теперь можна использовать это. Нам нужно сконфигурировать Grunt и задать ему задачу.

Вы сообщаете Grunt, что делать через конфигурационный файл с названием Gruntfile.js2

Совсем как наш package.json файл, наш Gruntfile.js имеет специальный формат, который должен соблюдаться. Не важно что там значит каждое слово — просто взгляните на пример использования:

module.exports = function(grunt) {

    // 1. Всё конфигурирование тут
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        concat: {
            // 2. Конфигурация для объединения файлов тут.
        }

    });

    // 3. Здесь мы сообщаем Grunt, что мы планируем использовать этот плагин:
    grunt.loadNpmTasks('grunt-contrib-concat');

    // 4. Мы сообщаем Grunt, что нужно делать, когда мы введём "grunt" в терминале.
    grunt.registerTask('default', ['concat']);

};

Теперь нам нужно создать эту конфигурацию. Документация может быть просто огромной. Давайте сосредоточимся на очень простом примере.

Как вы помните, у нас 3 JavaScript файла, которые нужно объединить. Укажим к ним пути в виде массива в src разделе (указываем пути в кавычках) и далее укажем результирующий файл в разделе dest. Наш результирующий файл может пока что не существовать. Он будет создан при запуске этой задачи и объединит все файлы.

Оба наших файла jquery.js и carousel.js — это библиотеки. Скорее всего мы не будем их трогать. Поэтому для порядка, будем держать их в папке /js/libs/. Наш global.js файл — это место, где мы пишем наш код, поэтому он будет прямо в /js/ папке. Теперь давайте сообщим Grunt найти все эти файлы и объединить их вместе в один файл с названием production.js.

concat: {   
    dist: {
        src: [
            'js/libs/*.js', // Все JS в папке libs
            'js/global.js'  // Какой-то файл
        ],
        dest: 'js/build/production.js',
    }
}

Внимание: в этой статье будет маленькие чанки конфигурационного кода как я уже привёл выше. Целью является сфокусировать внимание на важных вещах, поэтому может вначале будет трудно понять как определённый чанк размещается в большем файле. Если вы запутаетесь, то тогда смотрите полный файл.

С этой конфигурацией для объединения, перейдите в терминал и запустите команду:

grunt

Смотрите что произойдёт! Будет создан файл production.js и произойдёт объединение наших 3х файлов. Это был огого момент для меня! Почувствуйте колебание силы. Давайте ещё сделаем пару штукенций!

Минификация JAVASCRIPT с помощью GRUNT

Мы сделали столько подготовительной работы, добавили новые задания для лёгкого запуска Grunt. Всё, что нам нужно:

Найти Grunt плагин, который будет делать что нам нужно:

  • Изучить конфигурирование этого плагина
  • Написать рабочую конфигурацию для нашего проекта

Официальный плагин для минификации кода — это grunt-contrib-uglify. Прямо как мы делали недавно, давайте запустим NPM команду для его установки:

npm install grunt-contrib-uglify --save-dev

Далее внесём изменения в наш Gruntfile.js для загрузки плагина:

grunt.loadNpmTasks('grunt-contrib-uglify');

Далее сконфигурируем его:

uglify: {
    build: {
        src: 'js/build/production.js',
        dest: 'js/build/production.min.js'
    }
}

Изменим это дефолтное задание для запуска минификации:

grunt.registerTask('default', ['concat', 'uglify']);

Правда очень похоже на установки объединения файлов?

Запустим grunt в терминале и на выходе получим чудестый минифицированный JavaScript:

Минифицированный JavaScript

Этот production.min.js файл мы будем использовать в нашем index.html файле.

Оптимизируем изображения с помощью Grunt

Давайте перейдём дальше. Официальный плагин для минификации изображений — это grunt-contrib-imagemin. Установите его:

npm install grunt-contrib-imagemin --save-dev

Зарегистрируйте его в Gruntfile.js:

grunt.loadNpmTasks('grunt-contrib-imagemin');

Сконфигурируйте его:

imagemin: {
    dynamic: {
        files: [{
            expand: true,
            cwd: 'images/',
            src: ['**/*.{png,jpg,gif}'],
            dest: 'images/build/'
        }]
    }
}

Убедитесь, что он запущен:

grunt.registerTask('default', ['concat', 'uglify', 'imagemin']);

Запустите grunt и посмотрите как осуществляется чудестное сжатие изображений:

Сжатие изображений

Нравится мне всё это дело.

Автоматизация всего процесса

До этого момента мы делали очень полезные вещи, но есть пара моментов, которые сделают нас умнее и сделают некоторые вещи понятнее, в том числе Grunt:

  • Запускайте эти задания автоматически, когда их нужно запустить
  • Запускайте только те задания, которые нужно запускать в данный момент

Например:

  • Объединяйте и минифицируйте JavaScript, когда JavaScript изменяется
  • Оптимизируйте изображения, когда новое изображения добавлено или какие-то изменены

Мы можем сделать это наблюдая за файлами. Мы можем сообщить Grunt смотреть на изменения в определённых местах и, когда там происходят изменения, запускать определённые задания. Наблюдение осуществляется через официальный плагин grunt-contrib-watch.

Установите его сами. Это делается аналогично тому, как мы делали раньше. Мы сконфигурируем его указав определённые файлы (или папки, или оба варианта) для наблюдения. Под наблюдением, я имею в виду мониторинг, удаление или добавление файлов. Далее мы указываем ему какие задачи нужно выполнить при обнаружении изменений.

Мы хотим запустить наше объединение и минификацию при обнаружении любых изменений в папке /js/. Когда это происходит, мы должны запустить задачи связанные с JavaScript. А когда эти вещи происходят где-то в другом месте, то не нужно запускать JavaScript-заданий, таким образом:

watch: {
    scripts: {
        files: ['js/*.js'],
        tasks: ['concat', 'uglify'],
        options: {
            spawn: false,
        },
    } 
}

Всё пока вроде просто, правда? Одной непонятной штукой может показаться spawn. И знаете что? Я и сам не знаю что это. Из всего, что написано в документациии — я понял что это дефолтное значение. Такая вселенная разработки. Просто оставьте как есть и будет всё ок, либо же почитайте доки.

Внимание: Иногда очень разочаровывает, когда вроде всё понятно в уроке, а на практике ничего не полчается. Если вы не можете заставить Grunt запуститься после изменений, то скорее всего вы допустили ошибку в вашем Gruntfile.js файле. Это может выглядеть следующим образом в терминале:

Ошибки при запуске Grunt

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

Добавим GRUNT к нашей предобработке

Последним пунктом нашего списка вначале статьи — это использование Sass — ещё другое задание Grunt очень для нас подходит. Но подождите? Разве технически Sass не на Ruby? Действительно это так. Но есть ещё версия Sass для Node и таким образом не добавляет дополнительные зависимости в наш проект, поэтому мы будем использовать официальный плагин grunt-contrib-sass, который подразумевает, что вы установили Sass на своей машине. Если вы не установили, то следуйте инструкциям командной строки.

Что прикольное в Sass — это то, что он может делать объединение и минификацию самостоятельно. Поэтому для нашего маленького проекта мы можем компилировать наш global.scss:

sass: {
    dist: {
        options: {
            style: 'compressed'
        },
        files: {
            'css/build/global.css': 'css/global.scss'
        }
    } 
}

Не хотелось бы вручную запускать эту задачу. Уже у нас есть установленный плагин для наблюдения, давайте его использовать! В конфигурацию наблюдения добавим ещё одну подзадачу:

css: {
    files: ['css/*.scss'],
    tasks: ['sass'],
    options: {
        spawn: false,
    }
}

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

Давайте сделаем ещё одно действие (асолютно оправданное) и добавим LiveReload. С помощью LiveReload, вам не нужно каждый раз обновлять браузер. Обновление страницы происходит автоматически таким образом, что при изменении CSS, новые стили вставляются без обновления всей страницы (очень полезно при тяжеловесных сайтах).

LiveReload очень просто установить поскольку он уже встроен в наш плагин наблюдения. Всё что нужно сделать это:

Установите плагин для браузера. Добавьте сверху конфигурации наблюдения:

. watch: {
    options: {
        livereload: true,
    },
    scripts: {   
    /* etc */

Перезапустите браузер и нажмите на LiveReload иконку для активации. Обновите Sass файл и наблюдайте как страница автоматически изменится. Live reloading browser Live reloading browser Круто!

Предпочитаете видео?

Если вы любите учится по видео, то я сделал видеоурок для этой статьи, который опубликовал в статье CSS-Tricks: First Moments with Grunt

Поднятие уровня

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

Некоторые хардкорные ботаны могут посмеиваться над простотой изложенного здесь материала. Но я бы посоветовал им сбавить обороты. Даже такие простые вещи, которые мы здесь делаем имеют значительную ценность. И не забывайте это всё бесплатно и опенсорс, что просто потрясающе.

Вы можете поднять свой уровень добавив ещё пару полезных задач:

Запускать ваш CSS через Autoprefixer (5+ я советую) вместо препроцессорных дополнений. Писать и запускать JavaScript юнит тестов (например: Jasmine). Строить ваши спрайты изображений и SVG иконки автоматически, (пример: Grunticon). Запускать сервер, чтобы вы могли связать объекты с определённым файловым путём и использовать сервисы, которые требуют настоящий URL такие как TypeKit и другие, а также убрать необходимость использования других инструментов для этого, таких как MAMP. Устранять проблемы с кодом с помощью HTML-Inspector, CSS Lint или JS Hint. Вставлять новый CSS автоматически в браузер, каждый раз при изменении в стилях. Помогать вам коммитить или пушить в систему контроля версий, такой как GitHub. Добавлять номер версий к вашим объектам (переполнения кеша). Помогать вам размещать на тестовой и производственной среде (например: DPLOY). Вы можете поднять свой уровень, просто поняв как сам по себе работает Grunt:

Читайте Grunt Boilerplate автор Mark McDonnell. Читайте Grunt Tips and Tricks автор Nicolas Bevacqua. Организуйте ваш Gruntfile.js просто разделив его на несколько маленьких файлов. Просмотрите Gruntfile.js файлы других проектов и людей. Изучайте больше по Grunt копаясь в исходниках и изучая его API.

Давайте делится впечатлениями

Я думаю, некоторый делёж впечатлениями будет неплох для общего процесса обучения. Если вы устанавливаете Grunt в первый раз (или помните как это уже делали), то запоминайте особенно маленькие разочаровающие вещи, которые вам попадаются, но которые вы успешно преодолеете. Эти маленькие проблемы можете описать здесь в комментариях. Таким образом у нас будет маленький список возможных проблем и преодолеем их вместе.

  1. Может такое случится, что однажды кто-то сделает чудестное Grunt приложение для вашей ОС. Но я не уверен, что это скоро случится. Конфигурационные плагины — это важная вещь при использовании Grunt. Каждый плагин, чуть отличается в зависимости от того, что он делает. Это значит, что невозможно сделать толковый UI для каждого из плагинов. Возможно самое толковое, из созданных на данный момент, дополнение — это Grunt DevTools.

  2. На Gruntfile.js часто ссылаются как Gruntfile в документации и примерах. Не называйте его буквально Gruntfile — он тогда не будет работать.