Gulp для сборки приожений Angular 1.x
Немного про Gulp
Было время, когда мне очень нравился Grunt, а причина была проста - другие средства сборки Web-проектов только начинали развиваться (2013 год, если что). Вскоре я открыл для себя Gulp. В сравнении с Grunt это был действительно удобный инструмент, и я надолго остановился именно на нём.
Всё никак не найду время на изучение Angular 2 и Webpack, поэтому один из мелких проектов пишу на Angular 1.x (актуальная версия на момент написания статьи - 1.6.x). Кто писал на нём, знает, что все контроллеры, директивы, сервисы и прочие компоненты лучше размещать в отдельных файлах, однако, потом на странице нужно подключать каждый файл отдельно.
Мириться с этим было нельзя, я поискал на StackOverflow похожие запросы и выяснил, что многие сталкивались с этой проблемой. Я предлагаю своё решение, являющееся компиляцией из разных источников.
Установка пакетов
В каталоге проекта инициализируем npm, отвечаем на несколько простых вопросов, после чего ставим необходимые плагины:
npm init && npm install gulp gulp-rename gulp-uglify gulp-include gulp-watch --save
| Название | Описание |
|---|---|
| gulp-rename | Позволяет указать имя целевого файла, отличное от исходного. Используется для создания отладочных и минимизированных версий файлов. |
| gulp-uglify | Сжимает JS. Используется для создания минимизированных продуктовых версий. |
| gulp-include | Позволяет вставлять код из одного файла в другой подобно тому, как это делает директива #include> в C-подобных языках. Так же позволяет делать вызов require, при этом код будет вставлен только в том случае, если не вставлялся ранее. |
| gulp-watch | Отслеживает изменения в нужных файлах, при необходимости автоматически вызывая нужные Gulp Task |
gulpfile.js
В каталоге приложения так же создадим файл по имени gulpfile.js (регистр важен) со следующим содержимым:
(function (r) {
"use strict";
var gulp = r("gulp"),
uglify = r("gulp-uglify"),
include = r("gulp-include"),
rename = r("gulp-rename"),
sources = {
login: "./src/login.js",
app: "./src/app.js"
},
DEST = "./todoist/static/app/"
gulp.task("js-dev", function () {
// Сборка без минификации, специально для отладки
gulp.src(sources.login)
.pipe(gulp.dest(DEST));
gulp.src(sources.app)
.pipe(include())
.pipe(gulp.dest(DEST));
});
gulp.task("js-prod", function () {
gulp.src(sources.login)
.pipe(uglify())
.pipe(rename("login.min.js"))
.pipe(gulp.dest("./todoist/static/app/login.min.js"));
gulp.src(sources.app)
.pipe(include())
.pipe(uglify())
.pipe(rename("app.min.js"))
.pipe(gulp.dest(DEST));
});
gulp.task("watch", function () {
gulp.watch("./src/**/*.js", ["js-dev"]);
});
gulp.task("default", ["js-dev", "watch"]);
}(require));
Надеюсь, комментарии к тому, что тут происходит, не нужны.
Файлы приложения
Все исходные файлы приложения у меня лежат в src/, для контроллеров создан каталог controllers, для сервисов - services (директивы и фильтры пока не успел написать, поэтому и каталогов нет).
Так выглядит главный файл приложения:
(function (A) {
"use strict";
var app = A.module("todoist", ["ui.bootstrap", "ngRoute", "ngCookies"]);
app.config(["$interpolateProvider", "$httpProvider", "$routeProvider", function ($interpolateProvider, $httpProvider, $routeProvider) {
$interpolateProvider.startSymbol("{$");
$interpolateProvider.endSymbol("$}");
$httpProvider.defaults.xsrfCookieName = "csrftoken";
$httpProvider.defaults.xsrfHeaderName = "X-CSRFToken";
$httpProvider.defaults.headers.common.Authorization = "Basic";
$routeProvider.when("/", {
controller: "MainController",
templateUrl: "/ui/index.html"
}).when("/users", {
controller: "UserListController",
templateUrl: "/ui/users/list.html"
}).when("/users/:id", {
controller: "UserDetailController",
templateUrl: "/ui/users/detail.html"
}).otherwise({
redirectTo: "/login"
})
}]);
//=include ./controllers/*.js
//=include ./services/*.js"
}(this.angular));
При сборке вместо комментариев //=include ./controllers/*.js будет вставлено содержимое лежащих там файлов - как раз то, чего я и хотел добиться.
Последний шаг - подключить файл приложения в шаблоне Django:
<script src="{% static 'app/app.js' %}"></script>
Grunt
Введение
Надоело мне как-то перепроверять все CSS-файлы при изменении дизайна. Поискав немного, я остановился на LESS. Вместо того, чтобы управлять одним огромным файлом styles.css, LESS позволяет создать множество мелких файлов, каждый из которых отвечает за свою часть общей таблицы стилей. Однако, собирать всё вручную мне было лень, поэтому я решил для своего проекта использовать Grunt. Для него есть множество модулей, которые способны сильно облегчить жизнь Web-разработчика, в частности, компилировать множество LESS файлов автоматически, собирать и сжимать JavaScript'ы и т.д.
В данной статье пойдёт рассказ о том, зачем нужен Grunt, показаны примеры его использования и базовой настройки, а также мимоходом захвачено использование LESS для разработки каскадных таблиц стилей.
Для проекта, над которым я работаю, было принято решение создать в корневом каталоге папку /less, куда поместить следующие файлы:
bootstrap.less- объединяет в себе все остальные файлы. Изменяя содержимое этого файла легко управлять результирующей таблицей стилей.colors.less- содержит переменные, описывающие цвета, используемые на страницах, например,@red: #FF0000;.fonts.less- описывает используемые на страницах шрифты. В своём проекте я решил использовать символьный шрифт FontAwesome вместо того, чтобы рисовать иконки.
а также по одному файлу на каждый отдельный блок, например, визуальный компонент или целую страницу.
Установка
Целевой системой сегодня будет не Debian, а Windows 8. Установка всех средств в Debian, кроме NodeJS, производится аналогично - командами в консоли. Если хотите NodeJS в Debian Wheezy - прочитайте вот эту статью.
Скачиваем установщик с сайта проекта NodeJS и запускаем, при установке не забываем указать, что нам также понадобится менеджер пакетов (Node Package Manager - NPM).
Настройка проекта для использования Grunt
Существует два способа установки Grunt'а - глобально и локально, в проект. Я рекомендую сам Grunt установить глобально, а необходимые модули - в папку проекта. Её всегда можно добавить в исключения вашей СКВ.
Запускаем командную строку от имени администратора и вводим следующие команды:
npm install grunt -g
npm install grunt-cli -g
Первый пакет - это собственно Grunt, второй - утилиты командной строки, позволяющие работать с ним. Ключ -g означает, что установленные пакеты должны быть доступны глобально.
Перейдём в каталог нашего проекта (вместо %project_root_folder% подставьте нужное) и инициализируем его для использования Grunt'а:
cd %project_root_folder%
grunt init
Будет задано несколько вопросов, касающихся проекта, в частности, имя автора, версия, лицензия и т.д. На основе ответов будет составлен файл package.json, который при необходимости всегда можно изменить. например, в моём проекте он выглядит вот так:
{
"name": "tester",
"version": "0.0.0",
"description": "Система тестирования",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Dunaevsky Maxim",
"license": "MIT",
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-concat": "^0.5.0",
"grunt-contrib-less": "^0.11.4",
"grunt-contrib-uglify": "^0.5.1"
}
}
Теперь нам нужно будет установить необходимые модули, я ограничусь всего тремя:
concat- собирает несколько указанных файлов в один.less- компилирует LESS-файлы в каскадные таблицы стилей CSS.uglify- оптимизирует и сжимает JavaScript-код.
Установка производится очень просто:
npm install grunt-contrib-concat --save-dev
npm install grunt-contrib-less --save-dev
npm install grunt-contrib-uglify --save-dev
Ключ --save-dev означает, что пакеты должны быть также прописаны в файле package.json.
Пакеты установлены, самое время дать Grunt'у задание - скомпилировать LESS-файлы, склеить вместе все JavaScript'ы из каталога /src/ и сжать итоговый файл. Для этого нам понадобится файл Gruntfile.js. Обратите внимание, первая буква в названии файла должна быть большой. Создать его следует в корневом каталоге проекта.
Рассмотрим Gruntfile.js, которым пользуюсь я сам:
"use strict";
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
less: {
dev: {
files: {
'css/styles.css': 'less/bootstrap.less'
}
},
min: {
options: {
ieCompat: true,
compress: 2,
optimization: true
},
files: {
'css/styles.min.css': 'less/bootstrap.less'
}
}
},
concat: {
js: {
src: [
'src/*.js'
],
dest: 'js/ui.js'
}
},
uglify: {
build: {
src: 'js/ui.js',
dest: 'js/ui.min.js'
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['less:dev', 'concat:js', 'uglify']);
};
Первым делом определяются настройки для задач, а также указывается, откуда брать информацию о проекте (файл package.json).
Задание для сборки CSS из LESS-файлов содержит две секции - настройки для разработки (dev) и для релиза (min), когда важен маленький размер файла и оптимизация кода. В обоих заданиях указано, что нужно на основе файла bootstrap.less из каталога less собрать файл styles.css в каталоге css.
Задание для concat'а небольшое - ему нужно всего навсего взять все js-файлы в каталоге src и склеить в один - ui.js, положив его в каталог js.
Самое последнее задание - оптимизация и сжатие получившегося при склеивании файла с помощью утилиты uglify.
В конце файла производится загрузка необходимых модулей и выполнение их в порядке перечисления аргументов функции grunt.registerTask. Изменяя порядок следования аргументов можно управлять процессом сборки.
Закончив редактирование файла Gruntfile.js, запустите командую строку в текущем каталоге и выполните команду grunt. В случае ошибки в конфигурации или одном из файлов проекта будет выдано соответствующее сообщение. В противном случае вывод на экран будет выглядеть примерно так:
Running "less:dev" (less) task
File css/styles.css created: 0 B → 251 B
Running "concat:js" (concat) task
File js/ui.js created.
Running "uglify:build" (uglify) task
Done, without errors.