0,0
рейтинг
14 февраля 2015 в 22:23

Разработка → Приятная сборка frontend проекта tutorial

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

Статья не претендует на истину в последней инстанции, так как сегодня существует большое количество различных сборщиков и подходов к сборке, и каждый выбирает по вкусу. Я лишь поделюсь своими мыслями по этой теме и покажу свой workflow.

UPD (13 марта 2015): Заменил несколько плагинов на более актуальные + решил проблему с импортом CSS файлов внутрь SCSS


Мы будем использовать сборщик Gulp. Соответственно у вас в системе должен быть установлен Node js. Установку ноды под конкретную платформу мы рассматривать не будем, т.к. это гуглится за пару минут.
И для начала отвечу на вопрос — почему Gulp?

Из более или менее сносных альтернатив мы имеем Grunt и Brunch.

Когда я только начал приобщаться к сборщикам — на рынке уже были и Grunt и Gulp. Первый появился раньше и по этому имеет более большое коммьюнити и разнообразие плагинов. По данным с npm:
Grunt — 11171 пакет
Gulp — 4371 пакет

Но Grunt мне показался черезчур многословным. И после прочтения нескольких статей-сравнений — я предпочел Gulp за его простоту и наглядность.

Brunch — это сравнительно молодой проект, со всеми вытекающими из этого плюсами и минусами. Я с интересом наблюдаю за ним, но в работе пока не использовал.

Приступим:

Создадим папку под наш проект, например «habr». Откроем ее в консоли и выполним команду:

npm init

Можно просто нажать Enter на все вопросы установщика, т.к. сейчас это не принципиально.
В итоге в папке с проектом у нас сгенерируется файл package.json, примерно такого содержания:

{
  "name": "habr",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Немного видоизменим его под наши нужды:

{
  "name": "habr",
  "version": "1.0.0",
  "description": "",
  "author": "",
  "license": "ISC",
  "dependencies": {
      "gulp": "^3.8.11"
   }
}

В блоке dependencies мы указали что нам нужен gulp и тут же будем прописывать все наши плагины.

Плагины:

gulp-autoprefixer — автоматически добавляет вендорные префиксы к CSS свойствам (пару лет назад я бы убил за такую тулзу)
gulp-minify-css — нужен для сжатия CSS кода
browser-sync — с помощью этого плагина мы можем легко развернуть локальный dev сервер с блэкджеком и livereload, а так же с его помощью мы сможем сделать тунель на наш localhost, что бы легко демонстрировать верстку заказчику
gulp-imagemin — для сжатия картинок
imagemin-pngquant — дополнения к предыдущему плагину, для работы с PNG
gulp-uglify — будет сжимать наш JS
gulp-sass — для компиляции нашего SCSS кода
Не холивара ради
Я очень долгое время использовал в своей работе LESS. Мне очень импонировал этот препроцессор за его скорость и простоту изучения. Даже делал доклад по нему на одном Ростовском хакатоне. И в частности в этом докладе я не очень лестно отзывался о SASS.
Но прошло время, я стал старше и мудрее :) и теперь я приобщился к этому препроцессору.
Основой моего недовольства SASS — было то что я не пишу на руби. И когда то для компиляции SASS/SCSS кода — надо было тащить в проект руби, с нужными бандлами — что меня очень огорчало.
Но все изменилось с появлением такой штуки как LibSass. Это С/C++ порт компилятора для SASS. Плагин gulp-sass использует именно его. Теперь мы можем использовать SASS в нативном node окружении — что меня безгранично радует.

gulp-sourcemaps — возьмем для генерации css sourscemaps, которые будут помогать нам при отладке кода
gulp-rigger — это просто киллер фича. Плагин позволяет импортировать один файл в другой простой конструкцией
//= footer.html

и эта строка при компиляции будет заменена на содержимое файла footer.html
gulp-watch — Будет нужен для наблюдения за изменениями файлов. Знаю что в Gulp есть встроенный watch, но у меня возникли с ним некоторые проблемы, в частности он не видел вновь созданные файлы, и приходилось его перезапускать. Этот плагин решил проблему (надеюсь в следующих версиях gulp это поправят).
rimraf — rm -rf для ноды

Установим все плагины и на выходе получим такой package.json:

{
  "name": "habr",
  "version": "1.0.0",
  "description": "",
  "author": "",
  "license": "ISC",
 "dependencies": {
    "browser-sync": "^2.2.3",
    "gulp": "^3.8.11",
    "gulp-autoprefixer": "^2.1.0",
    "gulp-imagemin": "^2.2.1",
    "gulp-minify-css": "^1.0.0",
    "gulp-rigger": "^0.5.8",
    "gulp-sass": "^1.3.3",
    "gulp-sourcemaps": "^1.5.0",
    "gulp-uglify": "^1.1.0",
    "gulp-watch": "^4.1.1",
    "imagemin-pngquant": "^4.0.0",
    "rimraf": "^2.3.1"
  }
}


Bower

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

bower init

Можно так же Enter на все вопросы.
В конце мы получаем примерно такой файл bower.json:

{
  "name": "habr",
  "version": "0.0.0",
  "authors": [
    "Insayt <insait.rostov@ya.ru>"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ]
}

И модифицируем его до нужного нам состояния:

{
  "name": "habr",
  "version": "0.0.0",
  "authors": [
    "Insayt <insait.rostov@ya.ru>"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "normalize.css": "*",
    "jquery": "2.*"
  }
}

В блоке dependencies мы будем указывать зависимости нашего проекта. Сейчас просто для теста это normalize и jQuery (хотя я уже не помню когда начинал проект без этих вещей).
Ну и конечно установим их командой:

bower i

Ну а теперь самое интересное. Создадим структуру нашего проекта и настроим сборщик.

Структура проекта:


Это очень спорный момент. Конечно проекты бывают разные, так же как и предпочтения разработчиков. Стоит только взглянуть на сайт yeoman.io (кстати это очень классный инструмент, который предоставляет большое кол-во заготовленных основ для проекта со всякими плюшками. Однозначно стоит присмотреться к нему). Мы не будем ничего выдумывать и сделаем самую простую структуру.

Для начала нам понадобится 2 папки. Одна (src) в которой мы собственно будем писать код, и вторая (build), в которую сборщик будет выплевывать готовые файлы. Добавим их в проект. Текущая структура у нас выглядит так:



В папке src создадим типичную структуру среднестатистического проекта. Сделаем main файлы в папках js/ и style/ и создадим первую html страничку такого содержания.

index.html
<!DOCTYPE html>
<html>
<head lang="ru">
    <meta charset="UTF-8">
    <title>Я собираю проекты как рок звезда</title>
</head>
<body>
    <section class="header">
        Header
    </section>
    <section class="content">
        Content
    </section>
    <section class="footer">
        Footer
    </section>
</body>
</html>

Структура папки src теперь будет выглядеть так:



Тут все тривиально:
fonts — шрифты
img — картинки
js — скрипты. В корне этой папки будет только файл main.js, который пригодится нам для сборки. Все свои js файлы — надо будет класть в папку partials
style — стили. Тут так же в корне только main.scss, а рабочие файлы в папке partials
template — тут будем хранить повторяющиеся куски html кода
Все html страницы которые мы верстаем — будут лежать в корне src/
Добавим в partials первые js и scss файлы и напоследок — перейдем в корень нашего проекта и создадим там файл gulpfile.js. Вся папка проекта сейчас выглядит так:



Теперь все готово к настройке нашего сборщика, так что let's rock!

Gulpfile.js

Вся магия будет заключена в этом файле. Для начала мы импортируем все наши плагины и сам gulp

gulpfile.js
'use strict';

var gulp = require('gulp'),
    watch = require('gulp-watch'),
    prefixer = require('gulp-autoprefixer'),
    uglify = require('gulp-uglify'),
    sass = require('gulp-sass'),
    sourcemaps = require('gulp-sourcemaps'),
    rigger = require('gulp-rigger'),
    cssmin = require('gulp-minify-css'),
    imagemin = require('gulp-imagemin'),
    pngquant = require('imagemin-pngquant'),
    rimraf = require('rimraf'),
    browserSync = require("browser-sync"),
    reload = browserSync.reload;


Конечно, не обязательно делать это именно так. Существует плагин gulp-load-plugins который позволяет не писать всю эту лапшу из require. Но мне нравится когда я четко вижу что и где подключается, и при желании могу это отключить. По этому пишу по старинке.

Так же создадим js объект в который пропишем все нужные нам пути, чтобы при необходимости легко в одном месте их редактировать:

var path = {
    build: { //Тут мы укажем куда складывать готовые после сборки файлы
        html: 'build/',
        js: 'build/js/',
        css: 'build/css/',
        img: 'build/img/',
        fonts: 'build/fonts/'
    },
    src: { //Пути откуда брать исходники
        html: 'src/*.html', //Синтаксис src/*.html говорит gulp что мы хотим взять все файлы с расширением .html
        js: 'src/js/main.js',//В стилях и скриптах нам понадобятся только main файлы
        style: 'src/style/main.scss',
        img: 'src/img/**/*.*', //Синтаксис img/**/*.* означает - взять все файлы всех расширений из папки и из вложенных каталогов
        fonts: 'src/fonts/**/*.*'
    },
    watch: { //Тут мы укажем, за изменением каких файлов мы хотим наблюдать
        html: 'src/**/*.html',
        js: 'src/js/**/*.js',
        style: 'src/style/**/*.scss',
        img: 'src/img/**/*.*',
        fonts: 'src/fonts/**/*.*'
    },
    clean: './build'
};


Создадим переменную с настройками нашего dev сервера:

var config = {
    server: {
        baseDir: "./build"
    },
    tunnel: true,
    host: 'localhost',
    port: 9000,
    logPrefix: "Frontend_Devil"
};


Собираем html

Напишем таск для сборки html:

gulp.task('html:build', function () {
    gulp.src(path.src.html) //Выберем файлы по нужному пути
        .pipe(rigger()) //Прогоним через rigger
        .pipe(gulp.dest(path.build.html)) //Выплюнем их в папку build
        .pipe(reload({stream: true})); //И перезагрузим наш сервер для обновлений
});

Напомню, что rigger это наш плагин, позволяющий использовать такую конструкцию для импорта файлов:

//= template/footer.html

Давай те же применим его в деле!
В папке src/template/ — создадим файлы header.html и footer.html следующего содержания

header.html
<section class="header">
    Header
</section>


footer.html
<section class="header">
    Footer
</section>


а наш файл index.html изменим вот так:
<!DOCTYPE html>
<html>
<head lang="ru">
    <meta charset="UTF-8">
    <title>Я собираю проекты как рок звезда</title>
</head>
<body>
    //= template/header.html

    <section class="content">
        Content
    </section>
    
    //= template/footer.html
</body>
</html>


Осталось перейти в консоль и запустить наш таск командой:

gulp html:build

После того как она отработает — идем в папку build и видим там наш файл index.html, который превратился в это:

<!DOCTYPE html>
<html>
<head lang="ru">
    <meta charset="UTF-8">
    <title>Я собираю проекты как рок звезда</title>
</head>
<body>
    <section class="header">
        Header
    </section>
    <section class="content">
        Content
    </section>
    <section class="footer">
        Footer
    </section>
</body>
</html>

Это же просто восхитительно!

Помню как много неудобств доставляло бегать по всем сверстанным страничкам и вносить изменения в какую-то повторяющуюся на них часть. Теперь это делается удобно в одном месте.

Собираем javascript

Таск по сборке скриптов будет выглядеть так:

gulp.task('js:build', function () {
    gulp.src(path.src.js) //Найдем наш main файл
        .pipe(rigger()) //Прогоним через rigger
        .pipe(sourcemaps.init()) //Инициализируем sourcemap
        .pipe(uglify()) //Сожмем наш js
        .pipe(sourcemaps.write()) //Пропишем карты
        .pipe(gulp.dest(path.build.js)) //Выплюнем готовый файл в build
        .pipe(reload({stream: true})); //И перезагрузим сервер
});

Помните наш файл main.js?
Вся идея тут состоит в том, чтобы с помощью rigger'a инклюдить в него все нужные нам js файлы в нужном нам порядке. Именно ради контроля над порядком подключения — я и делаю это именно так, вместо того что бы попросить gulp найти все *.js файлы и склеить их.

Часто, при поиске места ошибки я по очереди выключаю какие то файлы из сборки, что бы локализовать место проблемы. В случае если бездумно склеивать все .js — дебаг будет усложнен.

Заполним наш main.js:

/*
 * Third party
 */
//= ../../bower_components/jquery/dist/jquery.js


/*
 * Custom
 */
//= partials/app.js

Именно так я делаю на боевых проектах. Вверху этого файла всегда идет подключение зависимостей, ниже подключение моих собственных скриптов.

Кстати, bower пакеты можно подключать через такой плагин как gulp-bower. Но я опять же не делаю этого, потому что хочу самостоятельно определять что, где и как будет подключаться.

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

gulp js:build  

И в папке build/js — мы увидим наш скомпилированный и сжатый файл.

Собираем стили


Напишем задачу для сборки нашего SCSS:

gulp.task('style:build', function () {
    gulp.src(path.src.style) //Выберем наш main.scss
        .pipe(sourcemaps.init()) //То же самое что и с js
        .pipe(sass()) //Скомпилируем
        .pipe(prefixer()) //Добавим вендорные префиксы
        .pipe(cssmin()) //Сожмем
        .pipe(sourcemaps.write())
        .pipe(gulp.dest(path.build.css)) //И в build
        .pipe(reload({stream: true}));
});

Здесь все просто, но вас могут заинтересовать настройки автопрификсера. По умолчанию он пишет префиксы необходимые для последних двух версий браузеров. В моем случае этого достаточно, но если вам нужны другие настройки — вы можете найти их тут.

Со стилями я поступаю так же как и с js, но только вместо rigger'a — использую встроенный в SCSS импорт.
UPD (13 марта 2015): У некоторых людей возникла проблема с импортом css файлов инлайн. Как оказалось, gulp-sass не умеет этого делать, и на выходе дает простой CSS импорт. Но этот вопрос решает наш плагин gulp-minify-css, который заменяет CSS импорт на содержимое файла.
Наш main.scss будет выглядеть так:
/*
* Third Party
*/
@import "../../bower_components/normalize.css/normalize.css";

/*
* Custom
*/
@import "partials/app";

Таким способом получается легко управлять порядком подключения стилей.
Проверим наш таск, запустив
gulp style:build


Собираем картинки


Таск по картинкам будет выглядеть так:

gulp.task('image:build', function () {
    gulp.src(path.src.img) //Выберем наши картинки
        .pipe(imagemin({ //Сожмем их
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [pngquant()],
            interlaced: true
        }))
        .pipe(gulp.dest(path.build.img)) //И бросим в build
        .pipe(reload({stream: true}));
});

Я использую дефолтные настройки imagemin, за исключением interlaced. Подробнее об API этого плагина можно прочесть тут.

Теперь, если мы положим какую-нибудь картинку в src/img и запустим команду:

gulp image:build

то увидим в build нашу оптимизированную картинку. Так же gulp любезно напишет в консоли сколько места он сэкономил для пользователей нашего сайта.

Шрифты


Со шрифтами мне обычно не нужно проводить никаких манипуляций, но что бы не рушить парадигму «Работаем в src/ и собираем в build/» — я просто копирую файлы из src/fonts и вставляю в build/fonts. Вот таск:

gulp.task('fonts:build', function() {
    gulp.src(path.src.fonts)
        .pipe(gulp.dest(path.build.fonts))
});


Теперь давайте определим таск с именем «build», который будет запускать все что мы с вами тут накодили:

gulp.task('build', [
    'html:build',
    'js:build',
    'style:build',
    'fonts:build',
    'image:build'
]);


Изменения файлов


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

gulp.task('watch', function(){
    watch([path.watch.html], function(event, cb) {
        gulp.start('html:build');
    });
    watch([path.watch.style], function(event, cb) {
        gulp.start('style:build');
    });
    watch([path.watch.js], function(event, cb) {
        gulp.start('js:build');
    });
    watch([path.watch.img], function(event, cb) {
        gulp.start('image:build');
    });
    watch([path.watch.fonts], function(event, cb) {
        gulp.start('fonts:build');
    });
});

С понимаем не должно возникнуть проблем. Мы просто идем по нашим путям определенным в переменной path, и в функции вызывающейся при изменении файла — просим запустить нужный нам таск.

Попробуйте запустить в консоли:

gulp watch

И поменяйте разные файлы.
Ну не круто ли?

Веб сервер


Что бы насладиться чудом livereload — нам необходимо создать себе локальный веб-сервер. Для этого напишем следующий простой таск:

gulp.task('webserver', function () {
    browserSync(config);
});

Тут даже нечего комментировать. Мы просто запустим livereload сервер с настройками, которые мы определили в объекте config. К тому же gulp вежливо откроет наш проект в браузере, а в консоль напишет ссылки на локальный сервер, и на тунель, который мы можем скинуть заказчику для демонстрации.

Очистка


Если вы добавите какую-нибудь картинку, потом запустите задачу image:build и потом картинку удалите — она останется в папке build. Так что было бы удобно — периодически подчищать ее. Создадим для этого простой таск

gulp.task('clean', function (cb) {
    rimraf(path.clean, cb);
});

Теперь при запуске команды
gulp clean

просто будет удаляться папка build.

Финальный аккорд


Последним делом — мы определим дефолтный таск, который будет запускать всю нашу сборку.

gulp.task('default', ['build', 'webserver', 'watch']);


Окончательно ваш gulpfile.js будет выглядеть примерно вот так.
Теперь выполним в консоли
gulp

И вуаля. Заготовка для вашего проекта готова и ждет вас.

Пара слов в заключение


Эта статья задумывалась как способ еще раз освежить в памяти тонкости сборки frontend проектов, и для легкости передачи этого опыта новым разработчикам. Вам не обязательно использовать на своих проектах именно такой вариант сборки. Есть yeoman.io, на котором вы найдете генераторы почти под любые нужды.
Я написал этот сборщик по трём причинам.
— Мне нравится использовать rigger в своем html коде
— Почти во всех сборках что я встречал — используется временная папка (обычно .tmp/), для записи промежуточных результатов сборки. Мне не нравится такой подход и я хотел избавиться от временных папок.
— И я хотел что бы все это было у меня из коробки.

Мою рабочую версию сборщика вы можете скачать на моем github.

Надеюсь, статья оказалась полезной для вас.

P.S. Обо всех ошибках, недочетах и косяках — пожалуйста пишите в личку.
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

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

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

  • +1
    gulp-rigger — это просто киллер фича. Плагин позволяет импортировать один файл в другой простой конструкцией


    круто, надо поробовать
    • 0
      В jade эту роль играет оператор include.
      • +1
        Разница в том, что rigger может работать в абсолютно любом файле с любым расширением.
        • –1
          Любой шаблонизатор помимо include реализует возможность помимо подключения файлов добавлять в шаблон helpers, экранирование символов по требованию, условную логику и прочие плюшки, а вот rigger я вижу выполняет лишь малость, поэтому не вижу смысла его использовать для простого импорта…
          • +2
            ну так rigger это и не шаблонизатор. Вся его функциональность — простой импорт чего угодно и куда угодно. И он в своем деле хорош :)
      • 0
        я в курсе как это делается в jade :)
    • +1
      А лучше сразу начать использовать gulp-preprocess (grunt-preprocess).
    • 0
      блин, ведь это правда офигенно, например sass не умеет импортировать обычный css, приходилось делать concat и в gulpfile.js прописывать вендорские css руками
      • 0
        как раз на днях с той же целью использовала gulp-rename, чтобы просто менять расширение css на scss )
      • +4
        Умеет. Надо использовать вот такую конструкцию
        @import "CSS:../../bower_components/normalize.css/normalize.css";
        
      • 0
        бедняжка… а вот less могет!
        • 0
          В Less так же нужна особая конструкция
          @import (css) "foo.css";
          

          или
          @import (inline) "foo.css";
          

          Так что разница невелика :)
      • 0
        А я подключил `wiredep` и он мне во все нужные места распихивает `html`, `css`, `sass` и `scss`, после чего `usemin` это всё собирает. Гораздо лучше ручного управления: после `bower install` система сборки делает всё сама, мне остаётся выполнить лишь действительно нужные действия – например, создать объект настройки jQuery-библиотеки.
    • 0
      Оставлю тут плагин, который для себя делал, может пригодится
  • +2
    И в итоге получились rails assets pipeline :)
    • +7
      Rails assets pipeline который не привязан к Rails :)
      • 0
        Тогда можно говорить, что получилась привязка к Gulp. )
      • +1
        sprockets, строго говоря, не привязан к рельсам. Я его с удовольствием использую из middleman'а (генератор статических сайтов).
    • +1
      Мам, смотри, Спрокетс для Ноды!
  • +3
    var gulp = require('gulp'),
        watch = require('gulp-watch'),
        prefixer = require('gulp-autoprefixer'),
        uglify = require('gulp-uglify'),
        cssmin = require('gulp-cssmin'),
        sass = require('gulp-sass'),
        sourcemaps = require('gulp-sourcemaps'),
        rigger = require('gulp-rigger'),
        imagemin = require('gulp-imagemin'),
        pngquant = require('imagemin-pngquant'),
        rimraf = require('rimraf'),
        connect = require('gulp-connect'),
        opn = require('opn');
    


    Ещё рекомендую gulp-load-plugins. Тогда весь код выше заменяется строчкой:

    var plugins = require('gulp-load-plugins')();
    


    Соответственно плагины будут доступны как plugins.autoprefixer, plugins.sass и т.д.
    • 0
      В статье есть про это
      Конечно не обязательно делать это именно так. Существует плагин gulp-load-plugins который позволяет не писать всю эту лапшу из require. Но мне нравится когда я четко вижу что и где подключается, и при желании могу это отключить. По этому пишу по старинке.

      + новым разработчикам все таки проще с такой записью знакомиться. Конечно кода получается больше, но зато его понятность выше. Но это исключительно мое имхо, поэтому и отметил в статье про альтернативу
  • –44
    В этих строчках заключена ошибка:
    <title>Я собираю проекты как рок звезда</title>
    
    И вот какая. В тексте допущены 2 грамматичческие ошибки. Содержание же текста говорит, что автор «подобен профессионалу своего дела». Суть не в том, что рок-звезда допустит подобные ошибки. Он(а) в своей деятельности вообще ничего не пишет, кроме муз.композиций. Но никто не может сказать, что в песнях звезда ошибается. В тексте же HTML автор допускает ошибки, противореча своему высказыванию. (Программисту вообще трудно утверждать, что он пишет без багов.) Поэтому, надо или утверждение изменить, или исправить ошибки.
    • +9
      В вашем комментарии заключена ошибка. И вот какая. В тексте допущена 1 грамматическая ошибка. Содержание же текста говорит, что автор «подобен профессионалу в области оценки текстов на грамотность». Суть не в том, что Grammar Nazi не допустит подобные ошибки. Он(а) в своей деятельности вообще ничего не пишет, кроме рецензий на авторские тексты. Но никто не может сказать, что в своих рецензиях Grammar Nazi ошибается. В тексте же комментария автор допускает ошибки, противореча своему высказыванию. (Комментатору вообще трудно утверждать, что он пишет без ошибок). Поэтому, надо или утверждение изменить, или исправить ошибки.

      Простите.
    • 0
      Ошибки редактор поправит)
  • 0
    Недавно занимался настройкой окружения под себя и в итоге оформил это как npm-модуль с возможностью развернуть новый проект: fmp.

    Разница с описанными вами процессами небольшая, только прикрутил возможность при создании нового поректа указать используемый css-препроцессор, мне приходится работать с разными, ну и еще по-мелочи. В ближайших планах — добавить вменяемый HTML-шаблонизатор с возможностью определять переменные на любом уровне и устанавливать их значение в подключающих файлах (смотрю в сторону Handlebars и Jade), минификацию файлов и еще мелочевок.
    • +1
      Вы не слышали про yeoman?
  • 0
    >…я и делаю это именно так, вместо того что бы попросить gulp найти все *.js файлы и склеить их.
    Заметил, что gulp склеивает их (у меня) в алфавитном порядке. И пользуюсь этим.

    >…при поиске места ошибки я по очереди выключаю какие то файлы из сборки…
    Добавил правило — исключать из сборки файлы с префиксами "-" и "_". Соответственно, что бы исключить файл из сборки достаточно его переименовать.
    • 0
      1) Все верно, он склеивает в алфавитном порядке. Но это же не удобно. Хочется сохранить для себя свободу в именовании файлов.
      2) Закоментировать строчку в rigger'e будет быстрей, чем переименовывать файл. Зачем лишние действия?
      • 0
        >Но это же не удобно.
        Наверное, тут скорее вопрос привычки/предпочтений.
        Можно много чего придумать. Как вариант — делать обязательный префикс с нумерацией, с определённым шагом.
        Файл же всё-равно копируется или сохраняется — на этих этапах и можно добавить префикс, после которого уже можно написать нечто осмысленное.

        >Закоментировать строчку в rigger'e будет быстрей, чем переименовывать файл.
        Мне кажется, что это от IDE больше зависит.
        Для изменения порядка сборки вам приходится менять местами куски кода, тогда как в моём случае — опять же достаточно переименования.
        Впрочем, я и не утверждаю, что мой способ всегда лучше. Он всего лишь удобнее для меня — не нужно прописывать каждый файл вручную, а порядок подключения виден в любом подобии файлового менеджера, где поддерживается сортировка по имени.
  • +1
    Вопрос — а почему для сборки в альтернативах вы не рассмотрели npm? Есть неплохая статья, сравнивающая gulp, grunt & npm. Я вот остановился на npm и остался просто в восторге от него.
    • 0
      Я думаю, что использовать специальный пакет — просто логичнее. Например gulp использует системы стримов, и файл пишется на диск только в самом конце, пройдя, зачастую, через несколько пайпов. В случае использования чистого npm придется все же что-то придумывать самому, так просто не получится.
      • +1
        Логично, да, но npm такой же специальный пакет, только нативный :)
        Насчет стримов — смотрите, если вам нужны программные стримы, такие, как в gulp — можно пойти путем gulp, создать файл для сборки build.js, где делать все, что угодно и как угодно, не ограничиваясь плагинами gulp, которые по сути обертки над пакетами npm. В том числе вы можете использовать и стримы, и gulp, и что угодно, без каких-либо ограничений.

        Но на самом деле это все достаточно сложно для повседневных задач, и практически всегда можно обойтись тасками в "scripts" с нативными пайпами вместо стримов.

        Чем это классно? У вас нет лишнего файла gulpfile.js в проекте; вы используете декларативную нотацию тасков, что весьма читабельно; новый пользователь может узнать все возможные операции для проекта прямо из package.json, в одном месте, что удобно; наконец, вы используете нативный инструмент, который гарантированно будет существовать долго и всегда включает все возможные пакеты, в отличие от систем сборки, где их может недоставать.
        • +1
          Я с некоторых пор переехал на npm scripts и абсолютно с вами согласен — package.json это отличная точка входа, чтобы узнать о проекте всё, что нужно.
        • +1
          На самом деле используя gulp — вы можете все также использовать что угодно. Например browserify, или del. Этим он и отличается от grunt.
          Пожалуйста расскажите мне, как вы с помощью scripts напишите следующий gulpfile:
          var gulp = require('gulp');
          var _if = require('gulp-if');
          // опущу подключение остальных плагинов
          
          var production = process.env.NODE_ENV === 'production';
          
          gulp.task('less', function () {
              gulp.src('src/less/**/*.less')
                  .pipe(plumber()) // не даем ошибкам всплывать
                  .pipe(_if(!production, sourcemaps.init())) // для сорсмепов в дев-режиме
                  .pipe(less()) // компилируем less
                  .pipe(autoprefixer()) // расставляем префиксы для браузеров
                  .pipe(_if(production, csso())) // в продакшене жмем
                  .pipe(_if(!production, sourcemaps.write())) // пишем сормепы в дев-режиме
                  .pipe(gulp.dest('public/css')) // пишем на диск
                  .pipe(_if(production, gzip())) // если продакшен - жмем gzip
                  .pipe(_if(production, gulp.dest('public/css'))) // и тоже записываем на диск
          });
          
          gulp.task('default', ['less'], function () {
              gulp.watch('src/less/**/*.less', ['less']);
          });
          

          И это только кусок для сборки less.
          • 0
            Проще простого:

            "scripts": {
              "build": "node build.js",
              "watch": "watch 'npm run build' ./src/**/*"
            }
            


            Хаха! :)

            А если серьезно — вы правы, c globules это не так просто сделать, особенно на windows (я пытался, не получилось, на windows много ограничений в shell — нет xargs, нет подстановки комманд, нет globules, нет find *.less -exec cat {} и тд и тп). В то же время на виндах и в gulp с globules не все гладко.

            Если же вам известны точки входа less, то все гораздо проще:

            "scripts": {
              "build-dev": "npm run lessc-dev && npm run build-js && cat public/css/*.css | gzip-size | pretty-bytes",
              "lessc-dev": "lessc src/less/main.less --source-map | autoprefixer > public/css/main.css",
              "lessc-production": "lessc src/less/main.less | autoprefixer | csso > public/css/main.css ",
              "build-js": "browserify src/js/index.js | ccjs - > public/js/index.js",
              "watch": "watch 'npm run lessc-dev' less/*.less"
            
              ...
            }
            


            В принципе, в небольших проектах точки входа всегда известны. Если же проект гигантский, то тут уж ничего не поделаешь — надо писать скрипт сборки. Но это уже дело предпочтения — писать сборку через gulp или через простые стримы с прямыми зависимостями.
            • 0
              * globs, а не globules
          • 0
            с grunt нельзя? правда?

            Скажите, зачем вам лишние проверки на енвайронмент, если они только усложняют логику сборки? Не проще ли сконфигурировать для каждого свой запуск если енвайронменты отличаются?
            • –1
              Нет, нельзя. В grunt вы пишите конфиг, а он отрабатывает. То есть шаг в лево-право — новый плагин. Тк gulp основан на нативных потоках, вы можете очень легко встроить свой обработчик посреди обработки любого файла.
              Нет, не проще. Если я что-то могу сделать без дублирования кода — я стараюсь это сделать именно так.
              Ну и грант с его устаревшей системой при каждом изменении файла запишет это изменение на диск. Круто!
              • –1
                jmreidy/grunt-browserify это вам к примеру. Не дублировать код — это прелестно, но писать почти в каждой строчке if'ы — гораздо хуже.
              • 0
                Кстати, насчет дублирования. Есть так называемое правило третьего, где рекомендуется фолдить клоны именно на третьем повторении. Почему не на втором — потому что два похожих куска еще не обязательно клоны, есть риск сфолдить преждевременно, потратив время впустую.

                В вашем случае, для того, чтобы изменить девелоперский пайплайн — надо глазами пройтись по всему обобщенному пайплайну, на каждом условии вычислять, к dev или к production оно относится. А если появятся переменная ci?

                Я к тому, что у вас скорее не про дублирование, а про разделение ответственности.
  • +4
    Вместо gulp-connect удобнее пользоваться browsersync.
  • +2
    Оказывается, у Gulp есть чёрный список плагинов, и в нём, например, значатся gulp-connect и gulp-cssmin
    • 0
      Ого, спасибо за список)
      На днях пробегусь по нему — и поправлю статью
  • 0
    Такой вопрос — стоит ли добавлять сборку (директория build/ в статье) под .gitignore?

    Ведь при инициализации в любом случае сборка происходит заново.
    • +1
      Если вы настроите сборку на сервере — то можете добавить в игнор.
  • +8
    Хочется вспомнить этот боянистый твит:
    image
    Но если честно сам пользуюсь и gulp, и grunt (разные проекты), вот только мне bower не зашел совсем — попытался, но это уже для меня перебор. Устанавливать JQuery через npm — перебор.
    Может кто-нибудь кто пользуется разъяснит — в чем реальный смысл от него? Не хипстерства ради, а реальное применение, которое что-то облегчает.
    • 0
      Пристрастился к bower после прочтения Bower: зачем фронтенду нужен менеджер пакетов.
    • +1
      Чем лезть на сайт и искать последнюю версию библиотеки, проще написать bower update jquery

      Плюс, имея исходник в bower или npm, можно сделать свою сборку, взяв только нужные модули.

      И более того, даже если не исключать ненужное, можно добиться меньшего размера при сборке.
      Например, webpack рекомендует собирать reactjs из npm.
      • 0
        Пока писал комментарий — ниже ответили более развернуто
    • 0
      Собственно, управление зависимостями. Вы можете в bower.json указать конкретные версии библиотек от которых зависит ваше приложение. Можете даже указать конкретный коммит в GIT-репозитории (иногда бывает нужно и такое, когда, например, в какой-то из веток/пулл-реквестов есть фикс, но автор либы не спешит выпускать новую версию).

      Указали — и всё, теперь одной командой можно выгрузить все зависимости. И скачаются именно те версии, с которыми ваше приложение точно работает.

      В проект приходит новый разработчик — bower install и у него есть всё.

      Собираем версию на продакшен — bower install, получили сборку с нужными зависимостям.

      И да, если нужный вам пакет (bootstrap, например) зависит от другого (jquery в случае с бутстрапом), то Bower автоматически скачает зависимости.

      Да, всё это будет не нужно, если вы будете ходить по сайтам, качать нужные версии и хранить их в репозитории. Но и обновление версий, и разрешение зависимостей — всё надо делать вручную. А это, в общем-то, довольно примитивная и нудная задача с которой лучше всего справляются машины.
      • 0
        А если нету никаких зависимостей? Я пользуюсь как правило только Dojo и без особого труда контролирую её версию (и тащу её с CDN или кладу в git). Пока не очень понимаю необходимость такого нагромождения технологий для frontend. Frontend бывает разный и для разных нужд.
        Очень напоминает бум батареек для Django или RoR, что в итоге ИМХО ни к чему хорошему не привело.
        • 0
          Тогда да, Bower не нужен. Как вы сами отметили, фронтенд бывает разный, для каких-то проектов достаточно одного HTML-файла с парой-тройкой ссылок на CDN, а для каких-то и Bower не помешает.
    • 0
      Если вы испольузуете модульность в JS с помощью browserify, например, то еще как имеет.

      npm install jquery
      

      и потом

      var $ = require('jquery');
      

  • 0
    Не пойму, как собирать зависимости при помощи gulp-bower? Ведь просто вытаскивать по маске все js файлы не вариант, в пакетах обычно уже идут и минифицированные версии и исходники. Структура опять же везде разная.
    • 0
      Я использую такой скрипт:
          gulp.src('app/bower-manifest.html')
              .pipe($.filter(manifestFilter))
              .pipe(wiredep(wiredepConfig))
              .pipe($.usemin({
                  js: [$.sourcemaps.init(), $.uglify(), $.rev(), $.sourcemaps.write('.')]
              }))
      

      где $ — ссылка на gulp-load-plugins, а manifestFilter — проверка для ускорения билда, что не добавились новые зависимости.
      • 0
        Спасибо за ответ, а за
        где $ — ссылка на gulp-load-plugins
        отдельное спасибо.

        В итоге накидал вот такой скрипт.
        'use strict';
        
        var gulp = require('gulp');
        var plugins = require('gulp-load-plugins')({
            pattern: ['gulp-*', 'main-*'] //это необходимо для плагина main-bower-files
        });
        
        var path = {
            build: {
                js: 'dist/js'
            }
        };
        
        //установка зависимостей
        gulp.task('bower', function() {
            return plugins.bower();
        });
        
        //сборка зависимостей
        gulp.task("bower-files", function(){
            gulp.src(plugins.mainBowerFiles())
                .pipe(plugins.uglify())
                .pipe(plugins.concat('all.js'))
                .pipe(gulp.dest(path.build.js))
        
        });
        


        gulp-bower нужен для установки зависимостей. А для обработки зависимостей нужен (gulp-bower-files) main-bower-files
        • 0
          Пока не понял только, как бы последовательно запустить эти две задачи)
        • 0
          У вас нет сортировки зависимости, например, поставить jquery или angularjs первым.
          Вот накидал свой велосипед на эту тему:
          gist.github.com/dshster/2535b4317e495d070bcb
          • 0
            Я правильно понял, в данном примере $.order(['angular/*.js'… ставит на первое место соответственно angular, затем все остальные в случайном порядке?

            Как-то это все не то :) Вот и получается, что самый просто выход, это как автор, прописывать очередность в main.js
            • 0
              Попробуйте wiredep. Он должен сам правильно разруливать зависимости
              • 0
                в нем есть одна проблема — wiredep будет генерировать новые теги script и link для всех библиотек. На деве это конечно не страшно, но на прод так пускать нельзя. В итоге придется для прода все равно собирать все в один файл, и скорее всего — это придется делать похожим способом, как в статье. Так что проще один раз прописать это дело и забыть как страшный сон :)
                • 0
                  Я понимаю это. :) Мой скрипт выше по ветке как раз все либы минифицирует в один файл. :)
            • 0
              Варианты? Во-первых не нужно прописывать все модули, нужно прописать только те, которые строго должны идти в первую очередь. Я не смог добиться, чтобы main-bower-files выводил в том порядке в котором прописаны в bower.json.

              Даже если в консоли выполнить команду bower list -p, то порядок вывода модулей будет другой.
          • 0
            main-bower-files выдает файлы из пакетов, уже отсортированных согласно дереву зависимостей («dependencies» в bower.json пакета).
            Но еще один просто огромный плюс main-bower-files это то, что он может использовать секцию «overrides» в bower.json, чтобы переопределить описание установленного пакета.
            Вот пример:

            "dependencies": {
              "angular": "1.3.13",
              "jquery":"1.2.2",
              "some-another-package": "0.0.1"
            },
            "overrides": {
              "some-another-package": {
                "dependencies": {
                  "jquery": "*"
                },
                "main": {
                  "development": ["some-another-package.js"],
                  "production": ["some-another-package.min.js"],    
                }
              }
            }
            


            Предположим, что пакет some-another-package использует jquery, но в зависимостях у него он не прописан.
            Тогда при обработке main-bower-files его файлы могут выпасть перед файлами jquery и будет у нас красная консоль.
            Мы принудительно указываем ему зависимость от jquery и также можем указать разные файлы пакета для разных версий сборки (версию сборки можно указать при вызове mainBowerFiles() в скрипте.

            И исходя из всего вышенаписанного открывается еще одна невиданная доселе возможность: указать в качестве зависимости любой файл из git-репозитория, впоследстии описав его зависимости, после чего он будет обработан mainBowerFiles() и включен в проект в правильном месте. Вот «живой» пример:

            "dependencies": {
               ...
              "flot-splines": "https://raw.githubusercontent.com/AMKohn/flot/master/jquery.flot.spline.js",
               ...
            },
            ...
            "overrides": {
              "flot-splines": {
                "main": [
                  "index.js"
                ],
                "dependencies": {
                  "flot": "*"
                }
              }
            } 
            


            Само собой, это грязный трюк — завтра реп могут благополучно грохнуть, но иногда он выручает просто безмерно.
  • 0
    Странно, что не используется ничего для AMD модулей.

    Ну и спорный вопрос насчет необходимости Bower. Достаточно долго живем только c npm проблем нет пока, у нас часть кода шарится между клиентом и сервером. При использовании Bower это бы только добавило проблем.
    • –4
      AMD модули уже не вымирании.
      • +1
        с чего это вдруг?
        • 0
          С чего это вдруг что?
          С чего это вдруг сейчас или вопрос о фундаментальных причинах вымирания AMD?
          • 0
            Давайте перейдем сразу к фундаментальным причинам.
            • 0
              Человек стремится делать проще и красивее.
              // amd
              define(['./foo', './bar'], (foo, bar) => {
                  console.log({foo, bar});
              });
              
              // common
              var foo = require('./foo');
              var bar = require('./bar');
              
              console.log({foo, bar});
              

              Ну и на замену amd приходит System, это факт. А на замену common, возможно, es-modules.
              • 0
                А почему нельзя сделать вот так?

                define((require) => {
                var foo = require('./foo');
                var bar = require('./bar');

                console.log({foo, bar});
                });
                • 0
                  А зачем сложнее если можно проще?
                  • 0
                    Ну мне проще дебажить requirejs, чем commonjs с сорсмапами.
                    • 0
                      Интересная ситуация. Однако я не могу себе вообразить такого.
            • 0
              Модули решают проблему модульности. Все 3 стандарта (ES6H, CJS, AMD) по сути себя дублируют.

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

              И вопрос, к примеру, AMD vs CJS это уже просто выбор сахара. А сахар CJS слаще.
              А любой транспайлер/сборщик сделает модули удобные для платформы.

              Тем более сборщикам и остальным инструментам проще работать с CJS.
              К примеру webpack. Он может «подключать» .less в файл .stylus обычным require.
              «everything is a module»

              CJS стал де факто стандартом nodejs. ES6H станет стандартом для нода и веба. И AMD тут места нет.
              Он просто стал не нужен как стандарт. Как техника экспорта/компиляции/транспайлинга ок. Но не стандарт.
              • 0
                Я ошибался. Думал, что AMD это общее описание принципа модульной загрузки скриптов (особенно, если учесть что requirejs поддерживает и commonjs формат). Поэтому оригинальный вопрос стоит читать как «Странно, что не используется ничего для JS-модулей»
    • 0
      Есть разница в подключении зависимостей Bower и NPM, последний для каждого модуля создаёт свою копию зависимостей, Bower — использует общие зависимости. Это сделано для того, чтобы не было ситуации с подключением одновременно разных версий jquery, например.
  • –4
    Половину хлама можно выбросить если использовать webpack для зависимостей.
  • 0
    А что потом происходит с этой папкой build, как она попадает на продакшен? Ее прямо в git/whatever коммитят, или же на продакшене запускается тот же самый скрипт сборки после обновления исходников?
    • 0
      Если настроите сборку на сервере — можно не коммитить. Но не всегда хорошо захламлять сервер тулзами для сборки
      • 0
        А репозиторий бинарниками? Тут палка о двух концах.
        Наверное по хорошему надо deploy-server делать промежуточный, который как раз будет все собирать.
        • 0
          Ну это уже от специфики проекта будет зависить и можно много чего напридумывать) По этому и не стал в статье затрагивать данный вопрос. Пусть каждый решает сам, как ему удобнее.
  • 0
    В списке не хватает плагина для создания спрайтов.
    • 0
      Спрайты устарели, нынче модно в dataURI всё кодировать в отдельный CSS.
      • 0
        Мода это конечно круто, но можно узнать, в чем выигрыш кодирования в dataurl? Сборка спрайтов у меня происходит автоматически через spritesmith, на выходе less файл с переменными названий спрайтов. Сам графический файл со спрайтами кешируется.
      • 0
        Не заметил этой моды. В основном встречаю спрайты или svg. При использовании плагинов для генерации спрайтов, удобство от использования dataURI совсем сомнительно, к тому же отдельная css с dataURI весит в полтора раза больше, чем спрайт.
        • 0
          к тому же отдельная css с dataURI весит в полтора раза больше, чем спрайт

          Может это потому что в png применяется сжатие по умолчанию, а для css оно применится только при передаче по сети?

          • 0
            Что-то я очень сомневаюсь что закодированный в Base64 сжатый PNG еще сожмётся при передаче, иначе был бы нехилый профит.
            • 0
              Провел эксперимент, с картинкой png (этой: xkcd.com/1445/)
              Размер: 11 597 байт,
              В архиве: 11 613 байт

              В виде строки base64
              Размер: 15 464 байт
              В архиве: 11 694 байт

              Разница после gzip менее процента.
              Попробовал поменять архиватор, тенденция сохранилась.

              PS: на всякий случай прогнал картинку через punypng: No savings
              • 0
                Другими словами смысла в экономии трафика никакого. В оптимизации автоматизации тоже выигрыша нет, осталось выяснить скорость рендеринга тысячи спрайтов в виде одной картинки или в виде отдельных dataurl.
                • 0
                  В плане удобства — каждому свое.

                  Мне, например, нравится base64, тем что:
                  * его можно использовать в виде повторяющегося фона
                  * не нужен отдельный элемент для иконки <i class="icon"></i>
                  * base64 делается нажатием одной кнопки и без настроек — можно обойтись без сборки вообще, используя онлайн-декодер
                  * удаление иконки происходит так же просто как удаление класса из css
                  * добавление одной иконки никак не повлияет на другие. Со спрайтом вполне может произойти так, что неопытный специалист добавит что-то свое, пережмет из png-24 в png-8, и не заметит что стало хуже.

                  В общем, по мне baseurl проще. А значит, при прочих равных — лучше.
                  • 0
                    Использую spritesmish

                    > не нужен отдельный элемент для иконки

                    Всё уже давно делают через псевдоклассы

                    > base64 делается нажатием одной кнопки и без настроек — можно обойтись без сборки вообще, используя онлайн-декодер
                    > удаление иконки происходит так же просто как удаление класса из css

                    Я кидаю иконку в папку иконок и вообще ничего не делаю дальше (bower сам подхватывает и перекодирует), в вашем же случае нужно искать css файл, искать класс нужный, менять его даже с автоматическим кодированием или удалять, а если файл довольно объёмный, то и редактор будет неспешно работать

                    Последний пункт вообще не актуальный. Единственный минус это первый пункт с повторяющимся фоном, но на практике уже давно не сталкивался когда мне нужно положить на фон графическую текстуру — градиенты и тени делаются через css, а большие изображения в спрайты не вношу.

                    В общем на мой взгляд надуманные проблемы, которые уже давно нормально решены.
                    • 0
                      Система сборки это не преимущество спрайтов, для base64 все так же настраивается.

                      Что есть такого у спрайтов, чтобы их до сих пор использовать?

                      PS: забыл упомянуть — встраивать через data:url можно и jpeg и анимированный gif.
                      • 0
                        Простота — настроил и забыл, только спрайты кидаешь в папочку. Я не вижу необходимости в base64 сейчас, зачем менять то, что отлично работает, не создаёт проблем и не требует каких-то ручных вмешательств?
                        • 0
                          Вам менять никто ничего не предлагает, тем более если уже настроено и работает. Используйте, если удобно.
                          Говорю же, каждому своё.
        • 0
          В действительности, я не отрицаю использование спрайтов: для каждого инструмента есть своя цель. Когда картинок немного, можно использовать и спрайт. Но чем больше становится спрайт, тем больше он потребляет оперативной памяти (и, мне кажется, здесь имеет место прогрессия). Это происходит из-за того, что спрайт – это повторённое многократно изображение, обрезанное в разных местах. Мало того, что техника довольно изощрённая, так ещё и браузеры, насколько мне известно, не трудятся оптимизировать здесь потребление ОЗУ.

          Когда я сравнивал производительность спрайтов в сравнении со встроенными изображениями год-два назад, я создал страницу с 50 значками и десятком изображений покрупнее. Результат был одинаковый на всех движках – Webkit@Safari, Gecko@Firefox, IE не трогал – встроенные изображения оказались быстрее.

          Да, встроенные изображения немного тяжелее, но выигрыш получается комплексным: на потреблении памяти, на количестве запросов (по сравнению с несколькими изображениями), а также отдельный CSS приходит с сервера немного более сжатым (я проверял с помощью встроенного в OS X архиватора).

          Подсказал мне перейти на встроенные изображения Андрей @ai Ситник, если что. Он, насколько я знаю, также применяет это в своих рабочих проектах.
          • 0
            В теории (сейчас лень проверять) data-uri сделает из изображения base64.
            Следовательно размер увеличится на 1/3 с копейками.

            С другой стороны, если картинка не очень оптимизированна, то gzip должен выдать меньший результат.
            Однако моя интуиция не верна:
            davidbcalhoun.com/2011/when-to-base64-encode-images-and-when-not-to/
            TL;DR. gzip+base64 дает тот же размер что и бинарка

            Выводы очевидны
  • 0
    «gulp-rigger» — насколько это тормозит сборку, файлы все-таки должны парсится?
    • 0
      Практически не тормозит. Он просто копирует содержимое, никак его не обрабатывая. Оверхед только на чтение файла
  • +3
    LibSass всем хорош, только больно уж отстаёт от основной ветки. В итоге часть самых вкусностей не работает.

    Нашёл для себя в качестве альтернативы Stylus, который:
    а) нативный,
    б) довольно быстрый,
    в) более гибкий.

    Рекомендую.
    • 0
      Тоже иногда думаю перейти на него обратно, тем более, что я с ним уже работал. Например, там есть killer feature – можно использовать ранее заданное свойство как переменную, типа того:

      selector
          width: 10px
          height: @width
  • 0
    Недавно делал нечто подобное, но без bower (мне не нравится, что bower тянет за собой содержимое всего репозитория вместо одного js-файла) и с шаблонизатором Handlebars (ведь даже на фронте часто проще заполнять пространство в цикле, например, а не писать голый html). Так же livereload работает веселее — он не перегружает всю страницу, если изменены стили, а только перегружает сами стили. Ну и я использовал grunt, хотя можно попробовать и на gulp перейти.
    Ну и вообще у меня как-то попроще получилось, хотя вроде все примерно то же самое)
    Вот, если кому интересно — github.com/mrTimofey/boilerplate
    • 0
      Очень странный довод в пользу отказа от Bower — выше уже всё обсудили много раз. Вообще не важно в каком виде тянутся bower-модули, из них вы собираете общий js/css файл, а всё остальное можно поместить в .gitignore. Зато при каждом разворачивании проекта у вас нужный набор всех зависимостей. Вместо Handlebars я предпочитаю Jade.

      Если у вас есть общий шаблон, на этом шаблоне построена куча сайтов и в какой-то библиотеке шаблона найдена уязвимость, то вам придётся ручками обновлять библиотеку на каждом сайте. Если сайтов 10, 20, 50 и они на разных хостингах, представьте сколько времени вам понадобиться чтобы обновить библиотеку? В общем тут только поможет опыт коллективной разработки сайтов чуть серьёзнее домашней странички.
      • 0
        Насчет Bower согласен. Стоит его добавить. Но жалко, конечно, что нельзя только нужный файл тянуть… Точнее, я читал, что как-то можно, но это будет не универсально, так как структура файлов проектов не у всех одинаковая.

        Jade — да, я его тоже пробовал на этом же boilerplate. Очень удобно, но его можно легко поставить вместо HB добавлением зависимости в package и небольшой правкой кода в engine.js (собственно, для этого я и вынес код для шаблонизатора в отдельный файл). Нужно будет поправить документацию с учетом возможности ставить свой шаблонизатор.
    • 0
      Если хотите устанавливать через bower только самые нужные файлы, можно использовать bower-installer: github.com/blittle/bower-installer
      • 0
        Чтобы что, просто интересно? «ignore» в конфиге со стандартными настройками вполне хватает, мне кажется.
        • +1
          Я лишь предложил решение проблемы, описанной mr_T. Так-то, мне тоже вполне достаточно «ignore» в конфиге)
  • 0
    Вдруг кому-то еще для быстрого старта и ознакомления пригодится мой конфиг Gulp-а с комментариями на русском для каждой задачи.
    В прошлом году познакомил с Gulp-ом коллег и некоторые уже активно используют, да и мне легче передавать проект.
  • 0
    здорово по моему
  • 0
    Обратил внимание, что генерируемый gulp-sourcemaps файл просто огромен (x10 от размера минифицированного файла). В опциях плагина ничего про сжатие карты не нашел, как победить такую ситуацию? И может быть, можно как-то ограничить пользователей от загрузки .map файла, он ведь им по факту не нужен?
    • 0
      Пользователь его без специального плагина и не получит (вы же его не подключаете как скрипт, я надеюсь).

      По мне, так для продакшена надо делать отдельную сборку, с минификацией и без сорсмап.
      • 0
        По-умолчанию конкретно этот соурсмап генерится прямо в минифицированном файле, в стоке-комментарии. Есть опция генерировать отдельным файлом, но в файле скрипта в комментарии прописывается путь, если файл .map не класть на продакшне, то будет 404.
  • 0
    Может простой вопрос, но всё же:
    почему пути src и watch отличаются?
    При том, что watch в итоге запускает билд по src.
    • 0
      Смотрите — например в стилях:
      Мы собираем только 1 файл, в который инклюдим все что нам нужно. Но сборку нам надо запускать при изменениях любого файла стилей. По этому в src один путь, а watch другой

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