Пользователь
0,0
рейтинг
24 июля 2012 в 10:02

Разработка → Grunt, инструмент для сборки javascript проектов перевод

Grunt — это инструмент для сборки javascript проектов из командной строки с использованием задач. Релиз вышел совсем недавно, автор Ben «Cowboy» Alman, проект есть на github. В этой статье я рассмотрю основы Grunt, его установку и использование.

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

Установка


Grunt устанавливается как NPM (Node Package Manager) модуль. Если у вас не установлены node.js и npm, то вам нужно установить их. Сделать это можно с официального сайта node.js или, если у вас Mac, используйте homebrew. Затем вам нужно установить npm — менеджер пакетов для node (вы можете провести параллель между npm и ruby gems). Обратите внимание, что если вы устанавливаете node.js с официального сайта, то npm идет в комплекте. Отдельно устанавливать npm нужно только если вы собирали node.js из исходников или использовали homebrew.
Непосредственно установка Grunt выполняется простой командой npm install -g grunt. Флаг -g означает установку глобально, то есть Grunt будет доступен всегда из командной строки, так как установлен в корневую папку node_modules. Если вы хотите запускать Grunt только в определенной папке, то находясь в ней, выполните ту же команду без флага -g. После успешной установки вывод консоли выглядит примерно так:

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

Инициализация


Сначала нужно инициализировать новый проект. Grunt содержит несколько удобных шаблонов, позволяющих инициализировать проекты типа commonjs, jquery и node. Для примера, давайте создадим jQuery-проект. Создайте папку для проекта и выполните в ней grunt init:jquery. Вам будет задано несколько вопросов. Значение по умолчанию Grunt показывает в скобках, и если вы не хотите его менять — просто нажмите enter. У меня это выглядело вот так:


Первый файл, который мы рассмотрим, это grunt.js (также его называют gruntfile). Его содержание может показаться не совсем понятным, не беспокойтесь об этом. Самое важно, что Grunt добавил в файл секцию qunit и создал папку test в проекте. Также он добавил инструкции по объединению файлов и слежению за файлами. Последнее означает автоматический запуск задач при любом изменении файлов:
watch: {
    files: '<config:lint.files>',
    tasks: 'lint qunit'
}

Эта задача использует файлы из config:lint.files, то есть ссылается на следующую секцию конфига:
lint: {
    files: ['grunt.js', 'src/**/*.js', 'test/**/*.js']
}

Grunt будет автоматически запускать задачи lint и qunit (которые делают именно то, что вы подумали), как только любой из этих файлов изменится. Весьма изящно! я продемонстрирую это чуть позже.
В конце файла вы видите следующее:
grunt.registerTask('default', 'lint qunit concat min');

Это инструкция для Grunt, что при запуске без параметров нужно выполнить lint, qunit, concat и min.

Запуск


Введите в терминале grunt и нажмите enter. К сожалению, у меня это сработало не так, как ожидалось:
Running "lint:files" (lint) task
Lint free.

Running "qunit:files" (qunit) task
Testing jquery.jsplayground-demo.html
Running PhantomJS...ERROR

Установка PhantomJS достаточно проста, инструкция есть тут. PhantomJS это консольный движок для Javascript (статья на хабре, прим. переводчика), который позволит нам запускать qunit тесты. После установки PhantomJS вывод консоли для команды grunt будет таким:

Итак, что же сделал этот скрипт:
  1. запустил проверку кода через JSLint
  2. автоматически запустил Qunit тесты, не открывая браузер
  3. объединил все файлы в один (хотя в этом примере у нас и так один файл)
  4. минифицировал объединенный файл

Не знаю как для вас, но для меня эта классный результат для всего одной команды!
Если, скажем, я захочу запускать все эти команды каждый раз, когда я изменил файлы, мне достаточно отредактировать grunt.js. Найдите секцию watch, которая выглядит примерно так:
watch: {
    files: '<config:lint.files>',
    tasks: 'lint qunit'
},

Я могу добавить сюда задачи concat и min, но как вы помните, мы определили задачу default, которая выполняет все эти действия. Поэтому при изменении файлов я могу просто запустить default:
watch: {
    files: '<config:lint.files>',
    tasks: 'default'
}

Конечно, в реальности запускать concat и min каждый раз при сохранении это слишком, я просто хотел показать, что это возможно. Вы можете создать несколько задач, одну для запуска по умолчанию, другую для запуска при релизе, третью для запуска в процессе разработки, и т.д.
Теперь давайте взглянем на js-файл, который был автоматически создан в src/jquery.jsplayground-demo.js. Внутри есть указание лицензии, copyright и ссылка на github — все это добавлено автоматически командой grunt init:jquery. Сейчас давайте внесем изменения в этот файл, чтобы увидеть watch в действии. Во-первых, нужно запустить watch в терминале: grunt watch. Теперь сделаем изменение: я собираюсь ввести невалидный javascript, чтобы мы увидели ошибку JSLint. Я напечатал в файле строку some rubbish stuff и сохранил его. Вывод в терминале автоматически обновился:


Сейчас я исправлю это, однако также удалю весь jQuery код, кроме $.fn.awesome. Grunt автоматически сгенерировал несколько тестов, поэтому когда я сохраню файл, вы увидите как тесты упадут. Так как тестируемый код будет удален.


А теперь я удалю лишние тесты, и все задачи успешно выполнятся:


Только представьте, насколько удобно работать над проектом, запустив grunt watch и зная, что весь код оттестирован, проверен и минифицирован.

Заключение


Надеюсь, этот небольшой обзор вдохновил вас на использование Grunt. Лично я использовал его на нескольких последних проектах и остался доволен.

Прим. переводчика: также советую посмотреть на проект grunt-contrib, в нем собраны наиболее часто встречающиеся задачи при сборке. Успехов!
Перевод: Jack Franklin
Виталий Потапов @vitalets
карма
44,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (15)

  • –14
    Не знаю… За все время работы с компьютером так и не смог заставить себя полюбить командную строку. Иногда без нее никак, но если есть выбор GUI vs CL, я всегда выбираю первое.
    • –8
      Ах да, это не пропаганда, я просто высказал свое отношение к командной строке )
    • +4
      Нам важно Ваше мнение, держите нас в курсе.
    • +1
      Какую ОС вы используете по-умолчанию?
  • +2
    Хотел спросить, умеет ли работать с CoffeeScript, но не поленился пройти по нижней ссылке и узнал, что таки работает. Крутая вещь. Спасибо.
    • 0
      В CoffeeScript принято использовать jsl, для которого предусмотрен ключ -l, --lint.
      Также есть CoffeeLint
  • +1
    Jquery собирается именно с этой штукой. Конфиг для jquery.
  • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    Спасибо за статью! Обязательно поиграюсь. В своих проектах я использую google closure compiler и все оптимизации производились именно им. Но то что вы описали звучит гораздо более интригующе
  • +1
    Как мне показалось, инструмент слишком уж «наворочен»: для чего ставить underscore, dateformat, соlors, gzip (по крайне мере ничего не сказано о выборочной установке)?
    Кому нужно и так поставят JSHint и Uglify, а заархивируют и без того встроенным в *nix систему gzip.

    Пользуясь случаем расскажу о своем сборщике файлов:

    #include <iostream>
    #include <list>
    #include "require.hpp"
    
    int main()
    {
    	Require require;
    
    	// Путь к файлам (можно не указывать, в этом случае он должен быть назначен самим файлам)
    	std::string path("./files/");
    
    	// Имена файлов, можно передать строкой с любым символом-разделителем
    	std::string name("file_1.js;file_2.js;");
    
    	// Записать в любой STL контейнер, который поддерживает итератор std::back_inserter
    	std::list<std::string> file;
    	require.split(name, ';', std::back_inserter(file));
    
    	// Можно без сплита сразу в контейнер добавлять элементы: file.push_back("file_1.js");
    
    	// Загружаем файлы
    	if (require.load(file, path)) { // устанавливаем имя файла и общий путь (опционально)
    
    		// Получаем данные
    		// Если установлен флаг минификации, то вырезаются все комментарии, пробелы, табуляция и  пере	воды строк
    		std::string data(require.data(true /* Можно задать флаг минификации */));
    
    		// Сохраняем данные в файл
    		if (require.save(path + "file.js"))
    			std::cout << data << std::endl;
    
    		// Если файл уже существует и не нужно его перезаписывать, то вторым параметром можно указать флажек
    		// require.save(path + "./file.js", std::ios::app);
            }
    
    	return 0;
    }
    


    В шаблонизаторе можно вывести данные прямо в поток:

    <script type="text/javascript">
    	<param _expr="require" _initparams="path=/files/js/, file=core.js;widget.js" />
    </script>
    


    Либо создавать отдельный подключаемый файл:

    <script type="text/javascript" _expr="require" _params="file as src" _initparams="path=/files/js/, file=core.js;widget.js"></script>
    


    У вас в шаблонизаторе, конечно это будет выглядеть иначе.

    Из плюсов могу назвать гибкость, скорость работы и возможность подключить как Node-модуль.
    Гибкость достигается за счет возможности передачи как списка параметров так и любого другого контейнера, а результат либо получается строка и бонус в ввиде файл с простой минификацией.

    После компиляции бинарник можно поставить в хуки VCS .

    Из минусов пока это, то что нет статического анализа кода, в следствии чего урезанная поддержка минификации и осутствие проверки кода на ошибки.
    • +1
      >>> Как мне показалось, инструмент слишком уж «наворочен»: для чего ставить underscore, dateformat, соlors, gzip (по крайне мере ничего не сказано о выборочной установке)?
      >>> Кому нужно и так поставят JSHint и Uglify, а заархивируют и без того встроенным в *nix систему gzip.

      Или поставят grunt — так проще. Grunt — это свой «make» для JavaScript-а, с батарейками в комплекте — в этом его огромный плюс: вам не надо писать скрипты сборки, загружать доп. библиотеки и делать прочую работу.
  • 0
    спасибо за перевод.
    а что касается самой статьи: можно было писать более подробно по самой утилите, а не по процессу «нажимаю на кнопку… нажал… не сработало....»
  • 0
    Хочется авторитетных мнений уважаемой публики по поводу вопроса —

    Кто-нибудь использует grunt в качестве замены питоновского fabric?

    Интересует та часть, которая про взаимодействие с ремотным хостом (ssh, scp, git, touch, ...), если вы понимаете о чем я.
    • 0
      Для grunt есть плагин для работы с SSH: npmjs.org/package/grunt-ssh

      Ну и конечно никто не мешает написать свой task (ну это на любителя :))
      • 0
        Да, плагин я видел этот, т.е. я правильно понял, что готовой замены Fabric пока нет.

        Ну ладно, будет время, перепишу свои несложные скиптики на grunt.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.