Возврат сброшенных css стилей. Стоит ли использовать CSS Reset

Всем привет, друзья! Сегодня мы подробно рассмотрим, что такое Gulp и как с его помощью можно автоматизировать работу Front-end разработчика. В результате урока мы соберем серьезное и внушительное рабочее Front-end окружение для веб-разработки с использованием Gulp.

Видео урок:

Класснуть

Запинить

  • Урок по обновлению Gulp до 4 версии: Ознакомиться с уроком Gulp 4
https://github.com/agragregra/gulp-lesson

Gulp - это инструмент, который помогает автоматизировать рутинные задачи веб-разработки. Gulp предназначен для решения таких задач, как:

  • Создание веб-сервера и автоматическая перезагрузка страницы в браузере при сохранении кода, слежение за изменениями в файлах проекта;
  • Использование различных JavaScript, CSS и HTML препроцессоров (CoffeeScript, Less, Sass, Stylus, Jade и т.д.);
  • Минификация CSS и JS кода, а также, оптимизация и конкатенация отдельных файлов проекта в один;
  • Автоматическое создание вендорных префиксов (приставок к названию CSS свойства, которые добавляют производители браузеров для нестандартных свойств) для CSS.
  • Управление файлами и папками в рамках проекта - создание, удаление, переименование;
  • Запуск и контроль выполнения внешних команд операционной системы;
  • Работа с изображениями - сжатие, создание спрайтов, ресайз (png, jpg, svg и др.);
  • Деплой (отправка на внешний сервер) проекта по FTP, SFTP, Git и т.д.
  • Подключение и использование в проекте безгранично большого количества Node.js и Gulp утилит, программ и плагинов.
  • Создание различных карт проекта и автоматизация другого ручного труда.

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

Любой проект, использующий Gulp имеет в корне файл gulpfile.js , который содержит набор инструкций по управлению проектом. Сразу хочется сказать, что написание инструкций для Gulp не является программированием, хотя пишутся на языке JavaScript. Не стоит пугаться больших gulpfile.js, в основном все инструкции однотипные и имеют общие черты. К тому времени, как вы прочтете данное руководство, у вас не должно остаться вопросов по Gulp, так как система сборки элементарная. Но если у вас останутся вопросы - обязательно пишите в комментариях.

Установка Gulp

Внимание! Если вы пользователь последней версии Windows, рекомендую использовать терминал Bash для веб-разработки. Скачивать инсталлятор с сайта Nodejs.org в этом случае не нужно. Воспользуйтесь этим руководством: .
Если у вас возникли проблемы при прохождении урока с использованием Gulp 4, рекомендую откатиться на 3 версию, пройти полностью урок и только после этого обновить package.json до 4 версии. Для лучшего понимания. Откатить версию можно в файле package.json. Вместо "gulp": "^4.x.x", напишите версию "^3.9.1", удалите папку "node_modules" и установите пакеты заново "npm i ".

Для работы с Gulp у вас должен быть установлен Node.js. Установка Node.js для различных платформ довольно простая - скачиваете инсталлер Node для своей операционной системы и устанавливаете. Я рекомендую устанавливать последнюю версию Stable. Для пользователей Linux и последней версии Windows я подготовил отдельное руководство по установке: Использование подсистемы Linux для веб-разработки в Windows 10 .

После того, как Node установлен, можно приступать к установке Gulp. Откройте терминал (правая кнопка мыши в папке с зажатым Shift > Откройте здесь оболочку Linux) и выполните следующую команду:

Npm i gulp -g

Для пользователей Mac и Linux и Ubuntu bash в Windows, глобальную установку с ключом -g необходимо выполнять с правами суперпользователя, sudo , например:
sudo npm i gulp -g .

Из данной команды мы видим, что запускается менеджер пакетов npm (Node Package Manager), который командой install устанавливает Gulp в систему. Ключ -g говорит о том, что пакет установится в систему глобально, то-есть в систему, а не в папку проекта. Без ключа -g пакет устанавливаются в ту папку, в которой выполняются текущие команды, поэтому будьте внимательны.

Создание проекта Gulp

Давайте создадим папку проекта для примера, с которой будем работать, пусть это будет, например, папка myproject .

Очень важно! Не создавайте русскоязычные папки проектов и следите за тем, чтобы путь до папки проекта не содержал кириллических символов, то-есть не был написан на русском языке. В противном случае, у вас могут возникнуть проблемы при работе различных утилит Gulp. Папка вашего пользователя также не должна быть русскоязычной. Всё только латинскими буквами.

Теперь откроем терминал в папке проекта. Для пользователей Windows достаточно зажать Shift и открыть контекстное меню. В нем появится пункт "Откройте здесь оболочку Linux". Оболочка Linux должна быть предварительно установлена, см урок: Использование подсистемы Linux для веб-разработки в Windows .

Npm init

Следуя инструкциям, заполним метаинформацию о нашем проекте:

В результате такой несложной первоначальной настройки нашего нового Gulp проекта в папке myproject нарисуется новый файл package.json .


Файл package.json является файлом манифеста нашего проекта, который описывает помимо той информации, что мы внесли в терминале, еще и информацию об используемых пакетах в нашем проекте.

Например, если мы установим в проект Gulp с ключом --save-dev , то пакет и используемая версия автоматически добавится в наш package.json. Такой учет позволит быстро разворачивать новый проект с использованием уже имеющегося package.json и устанавливать необходимые модули с зависимостями, которые прописаны в package.json в новых проектах.

Давайте установим в наш проект Gulp:

Npm i gulp --save-dev

Что мы видим из данной строки: npm устанавливает пакет gulp в текущую папку myproject (потому, что нет ключа -g, устанавливающий пакет глобально в систему) и сохраняет название пакета с версией в файл package.json:


Кроме того, у нас появляется папка node_modules , которая теперь содержит установленный пакет gulp и необходимые зависимости. В данную папку автоматически будут сваливаться все модули и зависимости, которые мы будем устанавливать в проект. Папок с зависимостями может быть очень много, не смотря на то, что мы установили не так уж и много пакетов. Это связано с тем, что в дополнение к основным пакетам устанавливаются программы, необходимые для корректной работы основного пакета. Ни чего чистить и удалять из папки node_modules не нужно. Кроме того, у вас может появиться дополнительный файл package-lock.json . В этом нет ничего страшного, это служебный файл, на который можно просто не обращать внимания.

Структура каталогов в проектах

Работая с различными плагинами, программами и скриптами, будь то jQuery плагин, модуль для CMS, веб-проект или какое-то другое ПО, вы наверняка замечали, что у всех проектов есть схожая структура каталогов, например, большинство проектов имеет папку dist и app . Давайте создадим первоначальную структуру нашего учебного проекта. В результате мы должны создать следующую структуру в нашем проекте myproject (все файлы, которых не было, пока создаем пустыми):

  • myproject/
    • app/
      • css/
      • fonts/
      • img/
      • js/
      • sass/
      • index.html
    • dist/
    • node_modules/
    • gulpfile.js
    • package.json
Данная структура встречается довольно часто, практически во всех проектах, но это не аксиома и некоторые проекты могут иметь вообще другую структуру. Для данной статьи мы будем использовать именно такую структуру проекта.

Здесь мы видим папку app/ , в которой будут размещены все исходные материалы проекта - оригинальные CSS, Sass, js файлы библиотек, оригинальные изображения. В общем - это папка исходников нашего проекта.

Папка dist/ будет содержать уже готовый продукт, оптимизированный, сжатый, причесанный. Это папка продакшена.

gulpfile.js

Теперь давайте откроем в редакторе кода gulpfile.js и напишем в него:

Var gulp = require("gulp");

Данной строчкой мы подключаем Gulp к нашему проекту, посредством функции require . Данная функция подключает пакеты из папки node_modules в наш проект, присваивая их переменной. В данном случае, мы создаем переменную gulp .

Gulp.task("mytask", function() { console.log("Привет, я таск!"); });

mytask - это название команды, которую вы будете вызывать в нужном вам месте gulpfile.js. Кроме того, можно в командной строке выполнить таск напрямую, написав:

Gulp mytask

gulpfile.js :


Результат выполнения команды gulp mytask :


Если вы используете Gulp 4 и у вас появляется ошибка о невозможности завершения таска, можно добавить async перед function() и выполнять код асинхронно: var gulp = require("gulp"); gulp.task("mytask", async function() { console.log("Привет, я таск!"); });

Это, конечно очень простой базовый пример создания таска. Как правило, таски несколько сложнее и включают некоторые дополнительные команды:

Gulp.task("mytask", function () { return gulp.src("source-files") // Выборка исходных файлов для обработки плагином.pipe(plugin()) // Вызов Gulp плагина для обработки файла.pipe(gulp.dest("folder")) // Вывод результирующего файла в папку назначения (dest - пункт назначения) })

  • gulp.src - выборка исходных файлов проекта для обработки плагином;
  • .pipe(plugin()) - вызов Gulp плагина для обработки файла;
  • .pipe(gulp.dest("folder")) - вывод результирующего файла в папку назначения (dest - пункт назначения).

Это база Gulp, теперь можно создавать инструкции. Для начала давайте создадим обработчик, который будет компилировать Sass файлы в CSS (CSS препроцессинг).

Gulp Sass

Давайте установим пакет gulp-sass в наш проект с сохранением версии и названия в package.json.

Обратите внимание, что любые Gulp пакеты, для любых задач, легко гуглятся и имеют вполне исчерпывающие инструкции по подключению на своих хоумпейджах и в документации.
npm i gulp-sass --save-dev

Var gulp = require("gulp"), sass = require("gulp-sass"); //Подключаем Sass пакет

Давайте создадим в папке app/sass файл main.sass , зададим в нем фон body - черный и напишем для него обработчик в gulpfile.js


gulpfile.js :

Var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"); //Подключаем Sass пакет gulp.task("sass", function(){ // Создаем таск "sass" return gulp.src("app/sass/main.sass") // Берем источник.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(gulp.dest("app/css")) // Выгружаем результата в папку app/css });

После этого, логичным будет выполнить в терминале наш новый таск sass :

Gulp sass

В результате выполения данной команды в папке app/css появится файл main.css .


От таки чудеса, друзя. Как видим, все просто:-)

Выборка файлов для gulp.src

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

Выборка файлов в примере выше довольно простая, мы брали файл напрямую: gulp.src("app/sass/main.sass") . Но файлы также можно выбирать по шаблону. Шаблон выборки файлов называется glob - https://en.wikipedia.org/wiki/Glob_(programming) . Давайте познакомимся ближе со всеми возможностями выборки файлов для обработки.

Самые распространенные шаблоны выборки

  • *.sass - выбирает все файлы, имеющие определенное расширение (в данном случае, .sass) в корневой папке проекта.
  • **/*.js - выбирает все файлы с расширением .js во всех папках проекта.
  • !header.sass - исключает файл из общей выборки
  • *.+(scss|sass) - задает комплексный шаблон для нескольких типов файлов, разделенных вертикальной чертой. В данном примере в выборкупопадут любые sass и scss файлы в корне проекта.

Давайте внесем некоторые изменения в таск sass и сделаем его более универсальным:

Gulp.task("sass", function(){ return gulp.src("app/sass/**/*.sass") // Берем все sass файлы из папки sass и дочерних, если таковые будут.pipe(sass()) .pipe(gulp.dest("app/css")) });

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

Обратите внимание, что файлы sass, которые предназначены для импорта в другие файлы, как части одного общего, начинаются с нижнего подчеркивания _part-1.sass . Такие файлы не учавствуют в компиляции, как отдельные файлы, а добавляются через @import в основные файлы.

Наблюдение за изменениями в файлах (Gulp Watch)

Gulp поддерживает метод watch для проверки сохраняемых файлов и имеет следующий синтаксис:

Gulp.watch("watch-files", ["task1", "task2"]);

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

Gulp.watch("app/sass/**/*.sass", ["sass"]);

Что мы видим: Gulp наблюдает за всеми sass файлами и при сохранении выполняет таск sass, который автоматически компилирует их в css файлы.

Также, мы можем создать отдельный таск для наблюдения за всеми необходимыми файлами

Gulp.task("watch", function() { gulp.watch("app/sass/**/*.sass", ["sass"]); // Наблюдение за sass файлами // Наблюдение за другими типами файлов });

Для Gulp 4 код будет выглядеть так: gulp.task("watch", function() { gulp.watch("app/sass/**/*.sass", gulp.parallel("sass")); });

Если мы запустим в консоли gulp watch , то Gulp будет автоматически следить за всеми измененями в файлах sass при сохранении и компилировать их в css.

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

Автоматическое обновление страниц с использованием Bbrowser Sync

Browser Sync - это отличное решение для LiveReload страниц при сохранении файлов. При чем релоад происходит не только в одном браузере, но и во всех браузерах сети, будь это мобильные устройства или другие компьютеры в одной Wi-Fi сети.

Мы уже умеем устанавливать дополнения для Gulp, поэтому давайте установим Browser Sync в наш проект:

Npm i browser-sync --save-dev

И, конечно-же, подключим в файле gulpfile.js, как мы это делали ранее с пакетом gulp-sass.

Var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"); // Подключаем Browser Sync

Создаем таск для Browser Sync:

Gulp.task("browser-sync", function() { // Создаем таск browser-sync browserSync({ // Выполняем browser Sync server: { // Определяем параметры сервера baseDir: "app" // Директория для сервера - app }, notify: false // Отключаем уведомления }); });

Отлично! Наш сервер для работы и автоматического релоада готов. Теперь давайте последим за изменениями в Sass. Если файл Sass обновляется, автоматически инжектим в HTML измененный CSS файл:

Gulp.task("sass", function(){ // Создаем таск Sass return gulp.src("app/sass/**/*.sass") // Берем источник.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(gulp.dest("app/css")) // Выгружаем результата в папку app/css .pipe(browserSync.reload({stream: true})) // Обновляем CSS на странице при изменении });

Все, что нам осталось сделать - это запустить таск browser-sync перед тем, как запустится gulp watch . Немного модифицируем таск watch , добавив выполнение browser-sync и sass до запуска watch :

Gulp.task("watch", ["sass", "browser-sync"], function() { gulp.watch("app/sass/**/*.sass", ["sass"]); // Наблюдение за sass файлами // Наблюдение за другими типами файлов });

Обратите внимание, что мы выполняем таски ["sass", "browser-sync"] до запуска watch , так как их выполнение необходимо нам для корректного отображения изменений на момент запуска сервера.
Для Gulp 4 логичнее было бы написать так и выполнять всю конструкцию в дефолтном таске: gulp.task("watch", function() { gulp.watch("app/sass/**/*.sass", gulp.parallel("sass")); }); gulp.task("default", gulp.parallel("sass", "browser-sync", "watch"));

Расположим таск watch после всех других тасков и в результате получим такой gulpfile.js для Gulp 3:

Var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"); // Подключаем Browser Sync gulp.task("sass", function(){ // Создаем таск Sass return gulp.src("app/sass/**/*.sass") // Берем источник.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(gulp.dest("app/css")) // Выгружаем результата в папку app/css .pipe(browserSync.reload({stream: true})) // Обновляем CSS на странице при изменении }); gulp.task("browser-sync", function() { // Создаем таск browser-sync browserSync({ // Выполняем browserSync server: { // Определяем параметры сервера baseDir: "app" // Директория для сервера - app }, notify: false // Отключаем уведомления }); }); gulp.task("watch", ["sass", "browser-sync"], function() { gulp.watch("app/sass/**/*.sass", ["sass"]); // Наблюдение за sass файлами // Наблюдение за другими типами файлов });

Такой код получится для Gulp 4:

Var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"); // Подключаем Browser Sync gulp.task("sass", function(){ // Создаем таск Sass return gulp.src("app/sass/**/*.sass") // Берем источник.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(gulp.dest("app/css")) // Выгружаем результата в папку app/css .pipe(browserSync.reload({stream: true})) // Обновляем CSS на странице при изменении }); gulp.task("browser-sync", function() { // Создаем таск browser-sync browserSync({ // Выполняем browserSync server: { // Определяем параметры сервера baseDir: "app" // Директория для сервера - app }, notify: false // Отключаем уведомления }); }); gulp.task("watch", function() { gulp.watch("app/sass/**/*.sass", gulp.parallel("sass")); // Наблюдение за sass файлами }); gulp.task("default", gulp.parallel("sass", "browser-sync", "watch"));

Для того, чтобы следить за изменениями в браузере, сделаем соответствующую разметку в файле index.html директории app с подключением файла стилей main.css :

Document

Выполним в терминале команду "gulp". Результат завораживает:


Давайте разберемся, что у нас происходит в консоли (картина может разниться, в зависимости от версии ПО):


После того, как мы нарадуемся результату, встает весьма ожидаемый вопрос - а как, собтвенно, обновлять страницу при сохранении HTML и JS?

И эта задача нам по плечу. Создайте в папке app/js файл common.js . Это основной пользовательский JS файл в нашем проекте. Модифицируем код:

Код для Gulp 3:

Gulp.task("watch", ["sass", "browser-sync"], function() { gulp.watch("app/sass/**/*.sass", ["sass"]); // Наблюдение за sass файлами в папке sass gulp.watch("app/*.html", browserSync.reload); // Наблюдение за HTML файлами в корне проекта gulp.watch(["app/js/common.js", "app/libs/**/*.js"], browserSync.reload); // Наблюдение за главным JS файлом и за библиотеками });

Код для Gulp 4 (здесь лучше добавить дополнительный таск для обработки HTML и JS):

Gulp.task("scripts", function() { return gulp.src(["app/js/common.js", "app/libs/**/*.js"]) .pipe(browserSync.reload({ stream: true })) }); gulp.task("code", function() { return gulp.src("app/*.html") .pipe(browserSync.reload({ stream: true })) }); gulp.task("watch", function() { gulp.watch("app/sass/**/*.sass", gulp.parallel("sass")); // Наблюдение за sass файлами gulp.watch("app/*.html", gulp.parallel("code")); // Наблюдение за HTML файлами в корне проекта gulp.watch(["app/js/common.js", "app/libs/**/*.js"], gulp.parallel("scripts")); // Наблюдение за главным JS файлом и за библиотеками }); gulp.task("default", gulp.parallel("sass", "browser-sync", "watch"));

Здесь мы используем функцию browserSync.reload, которую нам любезно предоставил пакет Browser Sync. Обратите внимание на выборку файлов для слежения.

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

Оптимизация JavaScript

Давайте рассмотрим, как можно оптимизировать JS файлы проекта. Чаще всего, в оптимизации нуждаются библиотеки и сторонние jQuery и JavaScript плагины. Давайте создадим в папке app паку libs , которая будет содержать необходимые проекту библиотеки. Все библиотеки будем размещать в отдельных папках. Для установки новых библиотек я советую использовать Bower .

Установим Bower:

Npm i -g bower

Обратите внимание, что для работы Bower необходим установленный Git . Если в ОС Windows вы используте оболочку Ubuntu bash, то установку Git выполнять не нужно.

Теперь в папке проекта создадим файл .bowerrc , в который напишем:

{ "directory" : "app/libs/" }

Если вы пользователь ОС Windows, у вас не получится просто взять и создать файл, начинающийся с точки. В этом случае можно просто поставить точку в конце файла и нажать Enter: .bowerrc.

Данной настройкой мы указываем путь по умолчанию для установки плагинов с помощью Bower.

Установим jQuery и Magnific Popup, для примера:

Bower i jquery magnific-popup

Обратите внимание, что все (ну, или почти все) плагины имеют папку dist, об этом мы говорили ранее. В этой папке располагаются готовые файлы продакшена, которые мы и будем использовать в нашем проекте.

Давайте создадим таск scripts , который будет собирать все JS файлы библиотек в один и минифицировать файл. Для этого установим 2 пакета: gulp-concat и gulp-uglifyjs .

Npm i --save-dev gulp-concat gulp-uglifyjs

Подключим новые библиотеки в gulpfile.js:

Var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"), // Подключаем Browser Sync concat = require("gulp-concat"), // Подключаем gulp-concat (для конкатенации файлов) uglify = require("gulp-uglifyjs"); // Подключаем gulp-uglifyjs (для сжатия JS)

Создаем задачу для сборки и сжатия всех библиотек (перед watch):

Gulp.task("scripts", function() { return gulp.src([ // Берем все необходимые библиотеки "app/libs/jquery/dist/jquery.min.js", // Берем jQuery "app/libs/magnific-popup/dist/jquery.magnific-popup.min.js" // Берем Magnific Popup ]) .pipe(concat("libs.min.js")) // Собираем их в кучу в новом файле libs.min.js .pipe(uglify()) // Сжимаем JS файл.pipe(gulp.dest("app/js")); // Выгружаем в папку app/js });

Давайте проверим, как работает наш новый таск scripts , выполнив в терминале:


Выполнение таска scripts можно запустить перед выполнением watch. Gulp 3:


Для Gulp 4 код будет выглядеть следующим образом - добавим в параллельное выполнение таска scripts (некоторую структуру уже задали ранее): gulp.task("watch", function() { gulp.watch("app/sass/**/*.sass", gulp.parallel("sass")); // Наблюдение за sass файлами gulp.watch("app/*.html", gulp.parallel("code")); // Наблюдение за HTML файлами в корне проекта gulp.watch(["app/js/common.js", "app/libs/**/*.js"], gulp.parallel("scripts")); // Наблюдение за главным JS файлом и за библиотеками }); gulp.task("default", gulp.parallel("sass", "scripts", "browser-sync", "watch"));

Далее можно подключить к проекту все необходимые CSS файлы библиотек. В нашем случае, только одна библиотека нуждается в подключении - это Magnific Popup. Сделаем это через @import в Sass фале sass/libs.sass :

@import "app/libs/magnific-popup/dist/magnific-popup.css" // Импортируем библиотеку Magnific Popup

Внимание! В новых версиях gulp-sass для импорта CSS файлов в Sass необходимо указывать расширение.css и импортировать CSS файлы в SASS файлы с нижним подчёркиванием в начале названия. Например, для того, чтобы импортировать файл library-name.css, необходимо создать вспомогатальный SASS файл, например, _libs.sass, импортировать в него нужный CSS - @import "app/libs/library-name.css" и добавить вспомогательный _libs.sass в главный main.sass без указания нижнего подчёркивания и расширения, например, так: @import "libs"

На выходе, в папке app/css мы получаем дополнительно к main.css файл libs.css, который содержит стили всех библиотек. Файл main.css нет особого смысла минифицировать, так как он содержит кастомные (пользовательские) стили. А вот файл libs.css мы с удовольствием минифицируем.

Внимание! Если в файле libs.css не появляется кода библиотек, а вы по-прежнему видите в нём конструкции @import, создайте отдельный файл _libs.sass для библиотек, который начинался бы с нижнего подчёркивания . Затем импортируйте этот файл в главный, тем самым объеденив и библиотеки и пользовательские стили в один файл.

Для минификации CSS установим пакеты gulp-cssnano и gulp-rename :

Npm i gulp-cssnano gulp-rename --save-dev

И подключим их в нашем gulpfile.js:

Var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"), // Подключаем Browser Sync concat = require("gulp-concat"), // Подключаем gulp-concat (для конкатенации файлов) uglify = require("gulp-uglifyjs"), // Подключаем gulp-uglifyjs (для сжатия JS) cssnano = require("gulp-cssnano"), // Подключаем пакет для минификации CSS rename = require("gulp-rename"); // Подключаем библиотеку для переименования файлов

И создадим соответствующий таск css-libs . Сразу добавим данный таск в watch для того, чтобы библиотеки собирались в процессе запуска проекта. Таск sass лучше вызвать до запуска css-libs, чтобы нам было что минифицировать:

Gulp.task("css-libs", ["sass"], function() { return gulp.src("app/sass/libs.sass") // Выбираем файл для минификации.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(cssnano()) // Сжимаем.pipe(rename({suffix: ".min"})) // Добавляем суффикс.min .pipe(gulp.dest("app/css")); // Выгружаем в папку app/css }); gulp.task("watch", ["browser-sync", "css-libs", "scripts"], function() { gulp.watch("app/sass/**/*.sass", ["sass"]); // Наблюдение за sass файлами в папке sass gulp.watch("app/*.html", browserSync.reload); // Наблюдение за HTML файлами в корне проекта gulp.watch(["app/js/common.js", "app/libs/**/*.js"], browserSync.reload); // Наблюдение за главным JS файлом и за библиотеками });

Код для Gulp 4:

Gulp.task("css-libs", function() { return gulp.src("app/sass/libs.sass") // Выбираем файл для минификации.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(cssnano()) // Сжимаем.pipe(rename({suffix: ".min"})) // Добавляем суффикс.min .pipe(gulp.dest("app/css")); // Выгружаем в папку app/css }); gulp.task("watch", function() { gulp.watch("app/sass/**/*.sass", gulp.parallel("sass")); // Наблюдение за sass файлами gulp.watch("app/*.html", gulp.parallel("code")); // Наблюдение за HTML файлами в корне проекта gulp.watch(["app/js/common.js", "app/libs/**/*.js"], gulp.parallel("scripts")); // Наблюдение за главным JS файлом и за библиотеками }); gulp.task("default", gulp.parallel("css-libs", "sass", "scripts", "browser-sync", "watch"));

Подготовка к продакшену

Результирующий код для Gulp 4 будет представлен в конце статьи.

Для продакшена (сборки в папку dist) мы создадим отдельный таск build в конце gulpfile.js. В данной инструкции мы осуществим сборку Sass, JS и выгрузку того, что у нас готово в папку dist.

Gulp.task("build", ["sass", "scripts"], function() { var buildCss = gulp.src([ // Переносим CSS стили в продакшен "app/css/main.css", "app/css/libs.min.css" ]) .pipe(gulp.dest("dist/css")) var buildFonts = gulp.src("app/fonts/**/*") // Переносим шрифты в продакшен.pipe(gulp.dest("dist/fonts")) var buildJs = gulp.src("app/js/**/*") // Переносим скрипты в продакшен.pipe(gulp.dest("dist/js")) var buildHtml = gulp.src("app/*.html") // Переносим HTML в продакшен.pipe(gulp.dest("dist")); });

Здесь, присваивая переменным какие-либо действия, мы их выполняем. Таким образом можно выполнять мультизадачные таски. Можно и не присваивать, но мы сделаем так, ибо красивше.

Все прекрасно, но всегда есть одно "Но". Перед тем, как собирать проект нам желательно бы очистить папку dist, чтобы не оставалось лишних потрохов от предыдущих итераций с нашим проектом.

Установим и подключим пакет del:

Npm i del --save-dev var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"), // Подключаем Browser Sync concat = require("gulp-concat"), // Подключаем gulp-concat (для конкатенации файлов) uglify = require("gulp-uglifyjs"), // Подключаем gulp-uglifyjs (для сжатия JS) cssnano = require("gulp-cssnano"), // Подключаем пакет для минификации CSS rename = require("gulp-rename"), // Подключаем библиотеку для переименования файлов del = require("del"); // Подключаем библиотеку для удаления файлов и папок

Создаем таск очистки clean и добавляем его выполнение перед выполнение build:

Gulp.task("clean", function() { return del.sync("dist"); // Удаляем папку dist перед сборкой }); gulp.task("build", ["clean", "sass", "scripts"], function() { var buildCss = gulp.src([ // Переносим библиотеки в продакшен "app/css/main.css", "app/css/libs.min.css" ]) .pipe(gulp.dest("dist/css")) var buildFonts = gulp.src("app/fonts/**/*") // Переносим шрифты в продакшен.pipe(gulp.dest("dist/fonts")) var buildJs = gulp.src("app/js/**/*") // Переносим скрипты в продакшен.pipe(gulp.dest("dist/js")) var buildHtml = gulp.src("app/*.html") // Переносим HTML в продакшен.pipe(gulp.dest("dist")); });

Для Gulp 4 попробуйте составить таски самостоятельно, как мы это делали в предыдущих прмерах.

Оптимизация изображений

Как вы могли заметить, в нашем проекте на продакшене не хватает изображений. Давайте исправим это недоразумение и добавим обработку изображений в наш проект. Данный раздел выполнен с использованием Gulp 3. Код для Gulp 4 можно адаптировать самостоятельно, как мы это делали ранее.

В папке app/img есть 3 изображения, которые нам необходимо перенести в папку продакшена, оптимизируя.


Для оптимизации изображений установим 2 пакета (gulp-imagemin , imagemin-pngquant ) и подключим их:

Npm i gulp-imagemin imagemin-pngquant --save-dev var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"), // Подключаем Browser Sync concat = require("gulp-concat"), // Подключаем gulp-concat (для конкатенации файлов) uglify = require("gulp-uglifyjs"), // Подключаем gulp-uglifyjs (для сжатия JS) cssnano = require("gulp-cssnano"), // Подключаем пакет для минификации CSS rename = require("gulp-rename"), // Подключаем библиотеку для переименования файлов del = require("del"), // Подключаем библиотеку для удаления файлов и папок imagemin = require("gulp-imagemin"), // Подключаем библиотеку для работы с изображениями pngquant = require("imagemin-pngquant"); // Подключаем библиотеку для работы с png

Gulp.task("img", function() { return gulp.src("app/img/**/*") // Берем все изображения из app .pipe(imagemin({ // Сжимаем их с наилучшими настройками interlaced: true, progressive: true, svgoPlugins: [{removeViewBox: false}], use: })) .pipe(gulp.dest("dist/img")); // Выгружаем на продакшен }); gulp.task("build", ["clean", "img", "sass", "scripts"], function() { var buildCss = gulp.src([ // Переносим библиотеки в продакшен "app/css/main.css", "app/css/libs.min.css" ]) .pipe(gulp.dest("dist/css")) var buildFonts = gulp.src("app/fonts/**/*") // Переносим шрифты в продакшен.pipe(gulp.dest("dist/fonts")) var buildJs = gulp.src("app/js/**/*") // Переносим скрипты в продакшен.pipe(gulp.dest("dist/js")) var buildHtml = gulp.src("app/*.html") // Переносим HTML в продакшен.pipe(gulp.dest("dist")); });

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

Установи м подключим gulp-cache :

Npm i gulp-cache --save-dev var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"), // Подключаем Browser Sync concat = require("gulp-concat"), // Подключаем gulp-concat (для конкатенации файлов) uglify = require("gulp-uglifyjs"), // Подключаем gulp-uglifyjs (для сжатия JS) cssnano = require("gulp-cssnano"), // Подключаем пакет для минификации CSS rename = require("gulp-rename"), // Подключаем библиотеку для переименования файлов del = require("del"), // Подключаем библиотеку для удаления файлов и папок imagemin = require("gulp-imagemin"), // Подключаем библиотеку для работы с изображениями pngquant = require("imagemin-pngquant"), // Подключаем библиотеку для работы с png cache = require("gulp-cache"); // Подключаем библиотеку кеширования

Модифицируем таск img :

Gulp.task("img", function() { return gulp.src("app/img/**/*") // Берем все изображения из app .pipe(cache(imagemin({ // Сжимаем их с наилучшими настройками с учетом кеширования interlaced: true, progressive: true, svgoPlugins: [{removeViewBox: false}], use: }))) .pipe(gulp.dest("dist/img")); // Выгружаем на продакшен });

Автоматическое создание префиксов CSS с помощью Gulp

Вендорные префиксы необходимы для обеспечения максимальной совместимости со всеми современными браузерами. Было бы логично сделать автоматическое добавление префиксов, чтобы написав в CSS или Sass:

Display: flex

Мы получили на выходе:

Display: -webkit-flex; display: -moz-flex; display: -ms-flex; display: -o-flex; display: flex;

Установим пакет gulp-autoprefixer и подключим его в gulpfile.js:

Npm i --save-dev gulp-autoprefixer var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"), // Подключаем Browser Sync concat = require("gulp-concat"), // Подключаем gulp-concat (для конкатенации файлов) uglify = require("gulp-uglifyjs"), // Подключаем gulp-uglifyjs (для сжатия JS) cssnano = require("gulp-cssnano"), // Подключаем пакет для минификации CSS rename = require("gulp-rename"), // Подключаем библиотеку для переименования файлов del = require("del"), // Подключаем библиотеку для удаления файлов и папок imagemin = require("gulp-imagemin"), // Подключаем библиотеку для работы с изображениями pngquant = require("imagemin-pngquant"), // Подключаем библиотеку для работы с png cache = require("gulp-cache"), // Подключаем библиотеку кеширования autoprefixer = require("gulp-autoprefixer");// Подключаем библиотеку для автоматического добавления префиксов

И модифицируем наш таск sass :

Gulp.task("sass", function(){ // Создаем таск Sass return gulp.src("app/sass/**/*.sass") // Берем источник.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(autoprefixer(["last 15 versions", "> 1%", "ie 8", "ie 7"], { cascade: true })) // Создаем префиксы.pipe(gulp.dest("app/css")) // Выгружаем результата в папку app/css .pipe(browserSync.reload({stream: true})) // Обновляем CSS на странице при изменении });

Дефолтный таск Gulp

Внимание! Дефолтный таск для Gulp 4 отличается от приведённого в этой главе. Полный код для Gulp 4 можно будет посмотреть в конце статьи.

Итак, мы имеем 2 главных таска - gulp watch - для работы над проектом в режиме "онлайн" и gulp build - для сборки проекта на продакшен без лишних файлов, папок и со сжатыми картинками. Так как чаще всего нам нужен будет таск watch , можно повесить его на дефолтный таск, чтобы не писать в консоли постоянно gulp watch, а писать просто gulp .

Gulp.task("default", ["watch"]);

Также, необходимо создать автономный таск для очистки кеша Gulp, чтобы его можно было вызывать простой командой gulp clear :

Gulp.task("clear", function () { return cache.clearAll(); })

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

В результате, у нас должен получиться такой gulpfile.js . Gulp 3:

Var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"), // Подключаем Browser Sync concat = require("gulp-concat"), // Подключаем gulp-concat (для конкатенации файлов) uglify = require("gulp-uglifyjs"), // Подключаем gulp-uglifyjs (для сжатия JS) cssnano = require("gulp-cssnano"), // Подключаем пакет для минификации CSS rename = require("gulp-rename"), // Подключаем библиотеку для переименования файлов del = require("del"), // Подключаем библиотеку для удаления файлов и папок imagemin = require("gulp-imagemin"), // Подключаем библиотеку для работы с изображениями pngquant = require("imagemin-pngquant"), // Подключаем библиотеку для работы с png cache = require("gulp-cache"), // Подключаем библиотеку кеширования autoprefixer = require("gulp-autoprefixer");// Подключаем библиотеку для автоматического добавления префиксов gulp.task("sass", function(){ // Создаем таск Sass return gulp.src("app/sass/**/*.sass") // Берем источник.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(autoprefixer(["last 15 versions", "> 1%", "ie 8", "ie 7"], { cascade: true })) // Создаем префиксы.pipe(gulp.dest("app/css")) // Выгружаем результата в папку app/css .pipe(browserSync.reload({stream: true})) // Обновляем CSS на странице при изменении }); gulp.task("browser-sync", function() { // Создаем таск browser-sync browserSync({ // Выполняем browserSync server: { // Определяем параметры сервера baseDir: "app" // Директория для сервера - app }, notify: false // Отключаем уведомления }); }); gulp.task("scripts", function() { return gulp.src([ // Берем все необходимые библиотеки "app/libs/jquery/dist/jquery.min.js", // Берем jQuery "app/libs/magnific-popup/dist/jquery.magnific-popup.min.js" // Берем Magnific Popup ]) .pipe(concat("libs.min.js")) // Собираем их в кучу в новом файле libs.min.js .pipe(uglify()) // Сжимаем JS файл.pipe(gulp.dest("app/js")); // Выгружаем в папку app/js }); gulp.task("css-libs", ["sass"], function() { return gulp.src("app/css/libs.sass") // Выбираем файл для минификации.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(cssnano()) // Сжимаем.pipe(rename({suffix: ".min"})) // Добавляем суффикс.min .pipe(gulp.dest("app/css")); // Выгружаем в папку app/css }); gulp.task("clean", function() { return del.sync("dist"); // Удаляем папку dist перед сборкой }); gulp.task("img", function() { return gulp.src("app/img/**/*") // Берем все изображения из app .pipe(cache(imagemin({ // С кешированием // .pipe(imagemin({ // Сжимаем изображения без кеширования interlaced: true, progressive: true, svgoPlugins: [{removeViewBox: false}], use: }))/**/) .pipe(gulp.dest("dist/img")); // Выгружаем на продакшен }); gulp.task("build", ["clean", "img", "sass", "scripts"], function() { var buildCss = gulp.src([ // Переносим библиотеки в продакшен "app/css/main.css", "app/css/libs.min.css" ]) .pipe(gulp.dest("dist/css")) var buildFonts = gulp.src("app/fonts/**/*") // Переносим шрифты в продакшен.pipe(gulp.dest("dist/fonts")) var buildJs = gulp.src("app/js/**/*") // Переносим скрипты в продакшен.pipe(gulp.dest("dist/js")) var buildHtml = gulp.src("app/*.html") // Переносим HTML в продакшен.pipe(gulp.dest("dist")); }); gulp.task("clear", function (callback) { return cache.clearAll(); }); gulp.task("watch", ["browser-sync", "css-libs", "scripts"], function() { gulp.watch("app/sass/**/*.sass", ["sass"]); // Наблюдение за sass файлами в папке sass gulp.watch("app/*.html", browserSync.reload); // Наблюдение за HTML файлами в корне проекта gulp.watch(["app/js/common.js", "app/libs/**/*.js"], browserSync.reload); // Наблюдение за JS файлами в папке js }); gulp.task("default", ["watch"]);

Результирующий код для Gulp 4:

Var gulp = require("gulp"), // Подключаем Gulp sass = require("gulp-sass"), //Подключаем Sass пакет, browserSync = require("browser-sync"), // Подключаем Browser Sync concat = require("gulp-concat"), // Подключаем gulp-concat (для конкатенации файлов) uglify = require("gulp-uglifyjs"), // Подключаем gulp-uglifyjs (для сжатия JS) cssnano = require("gulp-cssnano"), // Подключаем пакет для минификации CSS rename = require("gulp-rename"), // Подключаем библиотеку для переименования файлов del = require("del"), // Подключаем библиотеку для удаления файлов и папок imagemin = require("gulp-imagemin"), // Подключаем библиотеку для работы с изображениями pngquant = require("imagemin-pngquant"), // Подключаем библиотеку для работы с png cache = require("gulp-cache"), // Подключаем библиотеку кеширования autoprefixer = require("gulp-autoprefixer");// Подключаем библиотеку для автоматического добавления префиксов gulp.task("sass", function() { // Создаем таск Sass return gulp.src("app/sass/**/*.sass") // Берем источник.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(autoprefixer(["last 15 versions", "> 1%", "ie 8", "ie 7"], { cascade: true })) // Создаем префиксы.pipe(gulp.dest("app/css")) // Выгружаем результата в папку app/css .pipe(browserSync.reload({stream: true})) // Обновляем CSS на странице при изменении }); gulp.task("browser-sync", function() { // Создаем таск browser-sync browserSync({ // Выполняем browserSync server: { // Определяем параметры сервера baseDir: "app" // Директория для сервера - app }, notify: false // Отключаем уведомления }); }); gulp.task("scripts", function() { return gulp.src([ // Берем все необходимые библиотеки "app/libs/jquery/dist/jquery.min.js", // Берем jQuery "app/libs/magnific-popup/dist/jquery.magnific-popup.min.js" // Берем Magnific Popup ]) .pipe(concat("libs.min.js")) // Собираем их в кучу в новом файле libs.min.js .pipe(uglify()) // Сжимаем JS файл.pipe(gulp.dest("app/js")); // Выгружаем в папку app/js }); gulp.task("code", function() { return gulp.src("app/*.html") .pipe(browserSync.reload({ stream: true })) }); gulp.task("css-libs", function() { return gulp.src("app/css/libs.sass") // Выбираем файл для минификации.pipe(sass()) // Преобразуем Sass в CSS посредством gulp-sass .pipe(cssnano()) // Сжимаем.pipe(rename({suffix: ".min"})) // Добавляем суффикс.min .pipe(gulp.dest("app/css")); // Выгружаем в папку app/css }); gulp.task("clean", async function() { return del.sync("dist"); // Удаляем папку dist перед сборкой }); gulp.task("img", function() { return gulp.src("app/img/**/*") // Берем все изображения из app .pipe(cache(imagemin({ // С кешированием // .pipe(imagemin({ // Сжимаем изображения без кеширования interlaced: true, progressive: true, svgoPlugins: [{removeViewBox: false}], use: }))/**/) .pipe(gulp.dest("dist/img")); // Выгружаем на продакшен }); gulp.task("prebuild", async function() { var buildCss = gulp.src([ // Переносим библиотеки в продакшен "app/css/main.css", "app/css/libs.min.css" ]) .pipe(gulp.dest("dist/css")) var buildFonts = gulp.src("app/fonts/**/*") // Переносим шрифты в продакшен.pipe(gulp.dest("dist/fonts")) var buildJs = gulp.src("app/js/**/*") // Переносим скрипты в продакшен.pipe(gulp.dest("dist/js")) var buildHtml = gulp.src("app/*.html") // Переносим HTML в продакшен.pipe(gulp.dest("dist")); }); gulp.task("clear", function (callback) { return cache.clearAll(); }) gulp.task("watch", function() { gulp.watch("app/sass/**/*.sass", gulp.parallel("sass")); // Наблюдение за sass файлами gulp.watch("app/*.html", gulp.parallel("code")); // Наблюдение за HTML файлами в корне проекта gulp.watch(["app/js/common.js", "app/libs/**/*.js"], gulp.parallel("scripts")); // Наблюдение за главным JS файлом и за библиотеками }); gulp.task("default", gulp.parallel("css-libs", "sass", "scripts", "browser-sync", "watch")); gulp.task("build", gulp.parallel("prebuild", "clean", "img", "sass", "scripts"));

Проект-пример из данного урока вы можете посмотреть на GitHub и скачать: https://github.com/agragregra/gulp-lesson

Чтобы установить все пакеты и зависимости для скачанного примера, выполните команду npm i в папке проекта.

Помните - к любому плагину для Gulp есть хорошая документация по подключению и использованию на npmjs.com или на страничке GitHub.

Приветствую. Если вы занимаетесь frontend разработкой, то могли заметить, что часто приходится выполнять одни и те же задачи. Было бы здорово все это автоматизировать и свести объем рутины к минимуму. В этом вам могут помочь таск-менеджеры и сборщики проектов, такие как Gulp и Grunt.

Gulp – это ответвление от проекта Grunt. От своего родителя он взял лучшие практики. Код инструкций пишется на JavaScript. Работает быстрее, чем Grunt в несколько раз.

Gulp предоставляет по-настоящему широкие возможности. Он облегчит и ускорит frontend разработку. Перечислю основные задачи, которые сборщик проектов вам поможет решить.

  • Создание веб-сервера для отладки
  • Автоматическая перезагрузка страниц при внесении изменений (LiveReload)
  • Слежение за изменениями в файлах проекта
  • Использование препроцессоров HTML, CSS, JS
  • Объединение файлов и их минификация
  • Автоматическое создание вендорных префиксов для браузеров (Autoprefixer)
  • Автоматизация управления файлами и директориями
  • Запуск и контроль внешних команд операционной системы
  • Запуск и контроль приложений
  • Оптимизация изображений (сжатие, изменение размеров и т.д.)
  • Выгрузка проекта на внешний сервер с помощью FTP, SFTP, Git и т.д.
  • Подключение и использование дополнительных плагинов (сегодня их уже 3570 штук; решение можно
  • найти практически для всех повседневных рутинных задач и не только)
  • Автоматизация ручного труда

Установка

Для работы Gulp требуется Node.js. Скачать его можно на официальном сайте . Советую ставить LTS версию программной платформы. После инсталляции Node.js можно переходить к установке Gulp. Для этого откройте консоль операционной системы и выполните следующую команду:

Мы используем параметр -g , который позволяет установить Gulp глобально в операционной системе, без привязки к конкретному проекту.

Важно, чтобы в пути к директории установки не было русских символов. Из-за этого некоторые плагины gulp могут работать некорректно.

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

Это запустит скрипт, который задаст вам несколько вопросов о проекте. В результате будет сконфигурирован файл для npm package.json . Это манифест проекта. Он содержит список пакетов, которые используются в проекте и другую информацию. На этом этапе я внес следующие данные, адаптируйте под свой проект.

name: (bybtc-landing) version: (1.0.0) description: Landing Page for byBTC entry point: (index.js) test command: git repository: https://github.com/Neuropassenger/bybtc-landing.git keywords: landing

Если какой-то вопрос хотите пропустить, то просто нажимайте Enter. Будет использовано значение по умолчанию. Теперь можно установить Gulp для нашего проекта. Выполните команду:

npm i --save-dev gulp

Gulp будет установлен в директорию проекта, а параметр –save-dev добавит его в зависимости package.json. Это позволит другому разработчику, открывшему ваш проект, быстро развернуть его на своей машине (с помощью команды npm i ).

Если все прошло хорошо, то в папке проекта должен появиться каталог node_modules . Он содержит установленный пакет gulp и все зависимости, необходимые для его работы.

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

Создаем две папки в корне проекта:

  • /src/ – исходный код проекта во время разработки, здесь вы будете редактировать файлы
  • /dist/ – файлы и папки проекта после сборки, готовый продукт

Каталог /dist/ будет заполняться автоматически при сборке проекта. Займемся пока что /src/ . Создайте внутри следующие папки:

  • /css/ – для каскадных таблиц стилей
  • /js/ – для JavaScript-сценариев
  • /img/ – для изображений
  • /fonts/ – для шрифтов
  • /sass/ – для файлов препроцессора SASS (если используете SASS)
  • /libs/ – для сторонних библиотек

Если все готово, то пора перейти к созданию файла gulpfile.js в корне проекта, который поможет настроить Gulp. Именно здесь вы можете создать инструкции Gulp, которые помогут автоматизировать часть рутинных задач.

Инструкции Gulp

Любая инструкция создается в gulpfile.js с помощью функции gulp.task() . Первый параметр – это название инструкции. Затем идет массив из названий инструкций, которые нужно выполнить до запуска определяемой инструкции. Последний параметр – это функция, тело которой определяет то, что делает данная инструкция.

gulp.task("название_инструкции", ["инструкция_выполняющаяся_перед_текущей", "еще_одна_инструкция"], function() { // Какие-то действия });

Для вызова инструкции необходимо использовать в консоли следующую команду:

gulp название_команды

Компиляция SASS в CSS

Начнем с компиляции SASS в CSS. Установим пакет gulp-sass:

npm i --save-dev gulp-sass

Сначала необходимо подключить используемые пакеты в gulpfile.js . Сделаем это:

var gulp = require("gulp"), sass = require("gulp-sass");

Теперь создадим инструкцию, которая будет выполнять компиляцию SASS в CSS:

gulp.task("sass", function() { return gulp.src("src/sass/**/*.sass") .pipe(sass()) .pipe(gulp.dest("src/css")); });

В первой строке инструкции указываем исходные файлы для компиляции. В конкретном примере будут взяты все файлы с расширением .sass , находящиеся внутри папки /src/sass/ и ее подпапках. Можно выбирать и конкретные файлы. Вот примерный список того, как вы можете задавать пути к исходным файлам.

  • src/sass/main.sass – выбор файла main.sass
  • src/sass/*.sass – выбор всех файлов с расширением sass
  • src/sass/**/*.sass – выбор всех файлов с расширением sass во всех вложенных папках в папке sass
  • !src/sass/main.sass – исключение файла main.sass
  • [‘!src/sass/main.sass’, ‘src/sass/second.sass’] – исключение массива файлов main.sass и second.sass
  • src/sass/**/*.+(scss|sass) – выбрать все файлы scss и sass во всех вложенных папках в sass

Теперь создаем в папке /src/sass/ файл main.sass и определим в нем немного стилей:

body color: red font-size: 20px

Сохраняем файл. Теперь мы можем проверить, как работает компиляция. В консоли выполняем команду:

Проверяем каталог /src/css/ . В нем должен находится только что скомпилированный CSS файл. Видите? Отлично! Двигаемся дальше.

Автообновление страниц (LiveReload)

Перейдем к автоматизации обновления страниц при их изменении, т.е. настроим LiveReload . Это одна из самых популярных задач, стоящих перед . Нам понадобится пакет npm Browsersync для автоматического обновления браузера. Установим его:

npm i --save-dev browser-sync

Подключим browser-sync пакет в начале gulpfile.js , как мы это делали ранее с пакетами gulp и gulp-sass :

browserSync = require("browser-sync");

Создадим инструкцию для запуска Browsersync:

gulp.task("browser-sync", function() { browserSync({ server: { baseDir: "src" } }); });

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

Добавим еще один pipe к инструкции sass , с помощью которого будет происходить обновление стилей при компиляции CSS файлов. Указываем параметр stream: true . Это позволит обновлять стили потоково , без полной перезагрузки страницы.

Pipe(browserSync.reload({ stream: true; }))

Затем создадим инструкцию, которая будет следить за изменениями в файлах и перезагружать страницу при необходимости.

gulp.task("watch", ["browser-sync"], function() { gulp.watch("src/sass/**/*.sass", ["sass"]); gulp.watch("src/js/**/*.js", browserSync.reload); gulp.watch("src/**/*.html", browserSync.reload); });

Поясню. Перед запуском выполняется инструкция browser-sync , т.е. стартует веб-сервер для отладки проекта. После этого выполняется сама инструкция watch . Для слежения за изменениями в файлах используем gulp.watch() .

Внутри анонимной функции мы выполняем 3 раза gulp.watch с двумя параметрами. Первый параметр – файлы, за которыми нужно следить, второй – действия, которые нужно выполнить при изменении файлов, т.е. выполнить инструкцию sass или обновить страницу.

Обратите внимание на первый gulp.watch . Вместо browserSync.reload мы передаем в массиве инструкцию sass , которую нужно выполнить, если файлы были изменены. В ней, как вы помните, мы потоково обновляем стили на странице.

Минификация и объединение файлов

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

  • минифицировать (сжать) используемые библиотеки
  • уменьшить количество запросов к серверу, объединив библиотеки в единый файл

Добавим в исходные файлы проекта несколько библиотек. Для этого я использую Bower , пакет для NPM . Установим Bower:

Создаем файл конфигурации .bowerrc в корне проекта для Bower, где укажем ему, куда сохранять библиотеки:

{ "directory": "src/libs/" }

Установим, для примера, библиотеку jQuery и слайдер slick :

bower i jquery slick-carousel

Теперь можем заняться конкатенацией и сжатием библиотек. Для этого будем использовать пакеты gulp-concat и gulp-uglifyjs касательно JavaScript-файлов. Установим их:

npm i --save-dev gulp-concat gulp-uglifyjs

Касательно CSS – пакет gulp-cssnano . Устанавливаем:

npm i --save-dev gulp-cssnano

Минифицированные файлы обычно имеют суффикс .min . Добавить его нам поможет пакет gulp-rename . Устанавливаем:

npm i --save-dev gulp-rename

Начнем с подключения установленных плагинов в gulpfile.js :

concat = require("gulp-concat"), uglifyJs = require("gulp-uglifyjs"), cssNano = require("gulp-cssnano"), rename = require("gulp-rename");

JavaScript

Создадим инструкцию, которая позволит нам сжимать и объединять JavaScript-файлы:

gulp.task("min-js", function() { return gulp.src([ "src/libs/jquery/dist/jquery.min.js", "src/libs/slick-carousel/dist/slick.min.js" ]) .pipe(concat("libs.min.js")) .pipe(uglifyJs()) .pipe(gulp.dest("src/js")); });

Внутри анонимной функции инструкции min-js мы сначала указываем пути на JavaScript-файлы библиотек в виде массива. Затем с помощью concat объединяем библиотеки в единый файл libs.min.js uglifyJs . И, наконец, сохраняем результат в папку /src/js/ .

Инструкцию можно проверить с помощью команды в консоли:

В папке /src/js/ появится файл libs.min.js , в котором объединены и сжаты используемые в проекте JavaScript-файлы библиотек.

CSS

Создадим в каталоге /src/css/ файл libs.sass . Будем в него импортировать CSS-файлы библиотек. Для примера с помощью Bower я скачал библиотеку Bootstrap :

bower i bootstrap

Откроем файл libs.sass и импортируем в него CSS-файл Bootstrap:

@import "src/libs/bootstrap/dist/css/bootstrap"

Таким образом, мы соберем все CSS-файлы библиотек в одном месте, а именно в файле libs.sass с помощью импорта. Теперь создадим инструкцию для сжатия:

gulp.task("min-css", ["sass"] , function() { return gulp.src("src/css/libs.css") .pipe(cssNano()) .pipe(rename({ suffix: ".min" })) .pipe(gulp.dest("src/css")); });

Перед сжатием мы компилируем CSS из SASS с помощью инструкции sass , которую мы указали в массиве после имени инструкции min-css .

В первой строке мы берем конкретный файл, libs.css . Далее сжимаем его с помощью cssNano . Затем с помощью rename добавляем суффикс .min . Результат сохраняем в папке /src/css/ .

Проверяем инструкцию:

Если вы все сделали правильно, то в папке /src/css/ должно появиться два файла. libs.css и libs.min.css . Сравните их размеры.

Автоматическое добавление вендорных префиксов (Autoprefixer)

При использовании свежих возможностей CSS необходимо расставлять вендорные префиксы для правильной работы стилей. Делать такие вещи вручную – неблагодарное занятие. Поэтому заставим Gulp это сделать за нас.

Для начала установим gulp-autoprefixer :

npm i --save-dev gulp-autoprefixer

Подключим установленный пакет в gulpfile.js :

autoprefixer = require("gulp-autoprefixer");

Окей, теперь мы можем использовать autoprefixer в инструкции sass . Сделаем это после вызова .pipe(sass()) , т.к. вендорные префиксы нужно расставить после того, как SASS будет преобразован в CSS. Добавим новый pipe :

Pipe(autoprefixer([ "last 10 versions" ], { cascade: true }))

Первым параметром autoprefixer мы передаем массив, в котором указываем, что хотим включить поддержку последних 10 версий браузеров. Второй параметр – это настройки, где мы указываем, что хотим видеть красивый код на выходе, т.е. включаем каскадность.

Проверяем, добавляя в main.sass новое свойство flex . Запускаем инструкцию sass :

В main.css должны появиться вендорные префиксы. Очень просто, все работает в автоматическом режиме. Замечательно!

Финальная сборка проекта

Последнее, чего хотелось бы коснуться в этом гайде для новичков по Gulp – это финальная сборка проекта. Для этого нам понадобится папка /dist/ , которую мы создали в самом начале. Перед каждой сборкой ее необходимо очищать. Для этого будем использовать пакет NPM del . Установим его:

npm i --save-dev del

Подключим пакет del в gulpfile.js :

del = require("del");

Создадим инструкцию clean для очистки каталога / dist/ перед сборкой:

gulp.task("clean", function() { return del.sync("dist"); });

Теперь можно заняться непосредственно сборкой проекта. Создадим инструкцию build :

gulp.task("build", ["clean", "min-css", "min-js"], function() { var buildCss = gulp.src([ "src/css/libs.min.css", "src/css/main.css" ]) .pipe(gulp.dest("dist/css")); var buildFonts = gulp.src("src/fonts/**/*") .pipe(gulp.dest("dist/fonts")); var buildJs = gulp.src("src/js/**/*") .pipe(gulp.dest("dist/js")); var buildHtml = gulp.src("src/*.html") .pipe(gulp.dest("dist")); });

Перед вызовом инструкции build мы очищаем папку /dist/ на тот случай, если сборка уже проводилась до этого. Затем сжимаем и объединяем JavaScript и CSS файлы с помощью инструкций min-js и min-css соответственно . Попутно компилируем SASS в CSS, т.к. перед выполнением инструкции min-css выполняется инструкция sass .

Внутри тела инструкции мы копируем подготовленные файлы проекта в каталог с готовым продуктом /dist/ . Проверяем работу инструкции:

Все отлично работает! В папке /dist/ теперь находится готовый продукт после сборки, который можно выгружать на рабочий сервер.

Заключение

На этом закончу гайд для новичков по сборке проектов в Gulp. Как видите, все довольно просто. Со временем опубликую еще несколько постов, касающихся Gulp и его плагинов, как только сам хорошенько в них разберусь. А пока пользуйтесь и автоматизируйте свои рутинные задачи во frontend разработке согласно приведенной инструкции. Если появятся вопросы – задавайте в комментариях к посту.

{ "name": "bybtc-landing", "version": "1.0.0", "description": "Landing Page for byBTC", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/Neuropassenger/bybtc-landing.git" }, "keywords": [ "landing" ], "author": "Oleg Sokolov", "license": "ISC", "bugs": { "url": "https://github.com/Neuropassenger/bybtc-landing/issues" }, "homepage": "https://github.com/Neuropassenger/bybtc-landing#readme", "devDependencies": { "browser-sync": "^2.18.13", "del": "^3.0.0", "gulp": "^3.9.1", "gulp-autoprefixer": "^4.0.0", "gulp-concat": "^2.6.1", "gulp-cssnano": "^2.1.2", "gulp-rename": "^1.2.2", "gulp-sass": "^3.1.0", "gulp-uglifyjs": "^0.6.2" } }

var gulp = require("gulp"), sass = require("gulp-sass"), browserSync = require("browser-sync"), concat = require("gulp-concat"), uglifyJs = require("gulp-uglifyjs"), cssNano = require("gulp-cssnano"), rename = require("gulp-rename"), autoprefixer = require("gulp-autoprefixer"), del = require("del"); gulp.task("sass", function() { return gulp.src("src/sass/**/*.sass") .pipe(sass()) .pipe(autoprefixer([ "last 10 versions" ], { cascade: true })) .pipe(gulp.dest("src/css")) .pipe(browserSync.reload({ stream: true })); }); gulp.task("min-css", ["sass"] , function() { return gulp.src("src/css/libs.css") .pipe(cssNano()) .pipe(rename({ suffix: ".min" })) .pipe(gulp.dest("src/css")); }); gulp.task("min-js", function() { return gulp.src([ "src/libs/jquery/dist/jquery.min.js", "src/libs/slick-carousel/dist/slick.min.js" ]) .pipe(concat("libs.min.js")) .pipe(uglifyJs()) .pipe(gulp.dest("src/js")); }); gulp.task("browser-sync", function() { browserSync({ server: { baseDir: "src" } }); }); gulp.task("watch", ["browser-sync"], function() { gulp.watch("src/sass/**/*.sass", ["sass"]); gulp.watch("src/js/**/*.js", browserSync.reload); gulp.watch("src/**/*.html", browserSync.reload); }); gulp.task("clean", function() { return del.sync("dist"); }); gulp.task("build", ["clean", "min-css", "min-js"], function() { var buildCss = gulp.src([ "src/css/libs.min.css", "src/css/main.css" ]) .pipe(gulp.dest("dist/css")); var buildFonts = gulp.src("src/fonts/**/*") .pipe(gulp.dest("dist/fonts")); var buildJs = gulp.src("src/js/**/*") .pipe(gulp.dest("dist/js")); var buildHtml = gulp.src("src/*.html") .pipe(gulp.dest("dist")); });

Установка Gulp довольно простая. Для начала установите пакет Gulp глобально:

Npm install -g gulp

Затем установите его в свой проект:

Npm install --save-dev gulp

Использование Gulp

Давайте создадим задачу Gulp для минимизации одного из наших файлов JavaScript. Создайте файл с именем gulpfile.js. В нём будут определяться ваши задачи, которые запускаются с помощью команды gulp.

Добавьте следующие команды в ваш файл gulpfile.js:

Var gulp = require("gulp"), uglify = require("gulp-uglify"); gulp.task("minify", function () { gulp.src("js/app.js") .pipe(uglify()) .pipe(gulp.dest("build")) });

Установите gulp-uglify через npm выполнив npm install --save-dev gulp-uglify , а затем запустите задачу через gulp minify . Предположим, у вас есть файл с именем app.js в папке js, новый app.js будет создан в папке build и содержать сжатую версию js/app.js.

Что на самом деле здесь происходит?

Мы делаем несколько вещей в нашем файле gulpfile.js. Вначале мы загружаем модули gulp и gulp-uglify:

Var gulp = require("gulp"), uglify = require("gulp-uglify");

Затем определяем задачу с именем minify, которая при запуске вызывает функцию, заданную в качестве второго аргумента:

Gulp.task("minify", function () { });

В конце, и это самое сложное, мы определяем, что наша задача должна делать:

Gulp.src("js/app.js") .pipe(uglify()) .pipe(gulp.dest("build"))

Если вы не знакомы с потоками, а большинство фронтенд-разработчиков с ними не знакомы, то код выше ничего вам не скажет.

Потоки

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

В приведённом выше примере функция gulp.src() получает строку, которая соответствует файлу или набору файлов, и создаёт поток объектов представляющих эти файлы. Затем они переходят (или перетекают) в функцию uglify() , которая принимает объекты файлов и возвращает новые объекты файлов с минимизированным исходником. Этот результат затем перетекает в функцию gulp.dest() , которая сохраняет изменённые файлы.

Вот что происходит в виде схемы:

Когда есть только одна задача, функция ничего не делает. Тем не менее, рассмотрим следующий код:

Gulp.task("js", function () { return gulp.src("js/*.js") .pipe(jshint()) .pipe(jshint.reporter("default")) .pipe(uglify()) .pipe(concat("app.js")) .pipe(gulp.dest("build")); });

Чтобы запустить это самостоятельно, установите gulp, gulp-jshint, gulp-uglify и gulp-concat.

Эта задача берёт все файлы соответствующие js/*.js (иными словами все файлы JavaScript из папки js), запускает для них JSHint, выводит отчёт, минимизирует каждый файл, а затем объединяет их, сохраняя их в build/app.js. В виде схемы:

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

Для лучшего понимания потоков прочтите Stream Handbook .

gulp.src()

Функция gulp.src() берёт один или несколько файлов или массив и возвращает поток, который может быть передан в плагины.

Два других плагина понятнее: функция uglify() минимизирует код, а функция concat("app.js") объединяет все файлы в один с именем app.js.

gulp-load-plugin

Модуль, который я нахожу весьма полезным называется gulp-load-plugins, он автоматически загружает любые плагины Gulp из файла package.json и присоединяет их к объекту. Основное применение следующее:

Var gulpLoadPlugins = require("gulp-load-plugins"), plugins = gulpLoadPlugins();

Вы можете записать всё в одну строку (var plugins = require("gulp-load-plugins")(); ), но я не большой поклонник однострочного вызова require.

После запуска этого кода объект plugins будет содержать ваши плагины с именами в CamelCase-стиле (к примеру, gulp-ruby-sass будет загружен как plugins.rubySass ). Вы можете использовать их обычным путём. Например, наша задача js сократится так:

Var gulp = require("gulp"), gulpLoadPlugins = require("gulp-load-plugins"), plugins = gulpLoadPlugins(); gulp.task("js", function () { return gulp.src("js/*.js") .pipe(plugins.jshint()) .pipe(plugins.jshint.reporter("default")) .pipe(plugins.uglify()) .pipe(plugins.concat("app.js")) .pipe(gulp.dest("build")); });

К этому прилагается файл package.json, который содержит нечто похожее:

{ "devDependencies": { "gulp-concat": "~2.2.0", "gulp-uglify": "~0.2.1", "gulp-jshint": "~1.5.1", "gulp": "~3.5.6" } }

Данный пример на самом деле не намного короче. Однако для объёмных и сложных Gulp-файлов, это сократит блок с загрузкой файлов до одной или двух строк.

Версия 0.4.0 gulp-load-plugins выпущенная в начале марта добавила отложенную загрузку плагина, которая улучшает производительность. Плагины не загружаются, пока их не вызовем, это значит, что вам не придётся беспокоиться о неиспользованных плагинах в package.json влияющих на производительность (хотя их, вероятно, следует убрать в любом случае). Другими словами, если вы запустите задачу, которая требует только два плагина, она не станет загружать все плагины, которые необходимы для других задач.

Отслеживание файлов

Gulp имеет возможность следить за изменением файлов и выполнять задачу или задачи при обнаружении изменений. Эта функция удивительно полезна (для меня, наверное, одна из самых полезных в Gulp). Вы можете сохранить Less-файл, а Gulp превратит его в CSS и обновит браузер без каких-либо действий с вашей стороны.

Для слежения за файлом или файлами используйте функцию gulp.watch() , которая принимает шаблон файлов или их массив (такой как gulp.src() ), либо массив задач для их запуска или выполнения функции обратного вызова.

Предположим, что у нас есть задача build, она превращает наши файлы шаблонов в HTML и мы хотим определить задачу watch, которая отслеживает изменение шаблонов и запускает задачу для преобразования их в HTML. Мы можем использовать функцию watch следующим образом:

Gulp.task("watch", function () { gulp.watch("templates/*.tmpl.html", ["build"]); });

Теперь при изменении файла шаблона будет запущена задача build, которая создаст HTML.

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

Gulp.watch("templates/*.tmpl.html", function (event) { console.log("Event type: " + event.type); // добавлено, изменено или удалено console.log("Event path: " + event.path); // путь к файлу });

Другой отличительной особенностью gulp.watch() является то, что она возвращает watcher . Используйте его для прослушивания дополнительных событий или для добавления файлов в watch. Например, чтобы одновременно запустить список задач и вызвать функцию, вы можете добавить слушателя в событие change при возвращении watcher :

Var watcher = gulp.watch("templates/*.tmpl.html", ["build"]); watcher.on("change", function (event) { console.log("Event type: " + event.type); // добавлено, изменено или удалено console.log("Event path: " + event.path); // путь к файлу });

В дополнение к событию change вы можете прослушивать ряд других событий:

  • end
    Срабатывает, когда watcher завершается (это означает, что задачи и функции обратного вызова не будут больше вызываться при изменении файлов).
  • error
    Срабатывает, когда происходит ошибка.
  • ready
    Срабатывает, когда файлы были найдены и готовы к отслеживанию.
  • nomatch
    Срабатывает, когда запросу не соответствует ни один файл.

Объект watcher также содержит некоторые методы, которые можно вызывать:

  • watcher.end()
    Останавливает watcher (при этом задачи или функции обратных вызовов не будут больше вызываться).
  • watcher.files()
    Возвращает список файлов за которыми следит watcher .
  • watcher.add(glob)
    Добавляет файлы в watcher , которые соответствуют указанному шаблону glob (также принимает необязательную функцию обратного вызова в качестве второго аргумента).
  • watcher.remove(filepath)
    Удаляет определённый файл из watcher .
13 декабря 2017 в 17:40

Понимаем и работаем с gulp

  • JavaScript

Всем привет. Если вы связаны хоть как-то с JS, вы наверняка слышали о таком приложении как gulp . А возможно даже и использовали. По своему опыту могу сказать, что «въехать» в то, как с ним работать бывает сложно, хотя ключ к пониманию лежит на поверхности. Поэтому и публикую этот материал, надеясь, что он станет полезным.

Так же, на основе данного материала был снят видеоролик, так что можете выбирать, в каком виде потреблять.


Если сравнить gulp с другими популярными системами сборки, то это как сравнивать готовый квадрокоптер по типу “купил и полетел”, и набор для самостоятельной сборки дрона. Да, взлетите вы только на следующий день, но зато у ваших руках больше гибкости и контроля, особенно если у вас нестандартная задача.

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

Зайдем издалека. В экосистеме nodejs, существует такое понятие, как потоки , или stream. Из-за сложности перевода, потоками так же называются нити или threads многопоточной программы. В нашем же случае, поток - это объект, представляющий потоковые данные, и является совершенно иным понятием.

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

Const fs = require("fs"); const input = fs.createReadStream("myfile"); input.on("data", (chunk) => { console.log(chunk); }); input.on("end", () => { console.log("file is read"); });
Потоками в nodejs может быть практически все, начиная от файлов или строк заканчивая сокетами. Например, в известном фреймворке Express, HTTP запрос и ответ являются ни чем иным, как потоками. Потоки могут быть только на чтение, только на запись или и то и другое.

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

Это позволяет определить нужный поток данных (опять сложность перевода. Здесь имеется в виду flow, или течение) прямо здесь и сейчас не дожидаясь, когда данные станут доступны.

Например, вот так вот мы можем определить, что мы хотим отдать как результат, а “как” отдавать уже занимается сам движок.

Const fs = require("fs"); const express = require("express"); var app = express(); app.get("/", function (req, res) { fs.createReadStream("myfile") .pipe(res); }); app.listen(3000);
Обратите внимание, что обработчик запроса завершается до того, как файл даже откроется - остальное берет на себя движок ноды.

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

Если вы когда-нибудь слышали о vinyl - это как раз и есть реализация потоков, которые используют в gulp. Если мы возьмем стандартную задачу для галпа, и посмотрим что там внутри, то обнаружим, что на каждый вызов события data к нам приходит объект file, который и содержит всю необходимую информацию: имя файла, путь к файлу, рабочая директория и конечно же, его содержимое.

Const gulp = require("gulp"); gulp.task("default", function() { gulp.src("./*.js") .on("data", function(file) { console.log("data callback"); console.log(file.inspect()); /* It outputs: * data callback * > * data callback * > */ }) .pipe(gulp.dest("dist/")); });
Содержимое может быть представлено в двух форматах: в виде уже прочитанного буфера, или же в виде родного нодовского потока. Каждая ступень галповского пайпа берет на вход такие вот файлы, делает некую трансформацию и передает на выход следующей цепочке. Последняя цепочка обычно просто сохраняет их на диск.

Pipe(gulp.dest("dist/"));
Осознание факта того, что потоки в gulp другие ведет к просветлению и пониманию, поскольку это объясняет большинство проблем и ошибок.

Рассмотрим реальный пример. Вы хотите использовать browserify для склейки ваших JS файлов. Вы идете, и находите плагин gulp-browserify . Но видите приписку, которая говорит, что плагин deprecated, т.е. Устарел.

Как хорошо воспитанный программист вы отметаете этот вариант, и идете искать, а какое же решение не устарело. Находите официальные рецепты от gulp, и видите , что browserify работает с галпом напрямую. Ну как напрямую, через прослойку , которая как раз и переводит родной нодовский поток в виниловский поток, который понимает gulp. Без него ничего бы не заработало, поскольку это разные потоки.

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

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

  • Перевод

Данная статья - первая из цикла на тему укрощения CSS. Сегодня мы рассмотрим технологию CSS Reset .

Зачем это нужно?

Каждый браузер устанавливает свои значения стилей по умолчанию для различных HTML-элементов. С помощью CSS Reset мы можем нивелировать эту разницу для обеспечения кроссбраузерности стилей.

Например, вы используете элемент a в вашем документе. Большинство браузеров, как Internet Explorer и Firefox, добавляют ссылке синий цвет и подчёркивание . Однако представьте, что через пять лет кто-то решил создать новый браузер (назовём его UltraBrowser). Разработчикам браузера не нравился синий цвет и раздражало подчёркивание, поэтому они решили выделять ссылки красным цветом и полужирным шрифтом . Именно исходя из этого, если вы установите базовое значение стилей для элемента a , то он гарантированно будет таким, каким вы хотите его видеть, а не как предпочитают его отображать разработчики UltraBrowser.

Но теперь у нас вообще нет никаких отступов, в том числе между отдельными параграфами! Что делать? Не врать и не бояться: ниже нашего сброса мы опишем нужное нам правило. Сделать это можно разными способами: указать отступ снизу или сверху параграфа, указать его в процентах, пикселях или в em.

Самое главное, браузер теперь играет по нашим правилам, а не мы по его. Я решил сделать подобным образом:

* { margin: 0; padding: 0; } p { margin: 5px 0 10px 0; }

В итоге у нас получилось то, что можно увидеть в третьем примере .

Вы можете создать собственные стили для сброса, если вы решаете какую-то конкретную задачу в своём проекте. Несмотря на это, не существует пошагового руководства по созданию собственного CSS Reset. Опирайтесь на собственные принципы и собственный стиль.

Чтобы помочь вам правильно сделать выбор, приведу ещё пару ссылок:

  1. Less is more - my choice of Reset CSS (Эд Эллиот).

2. Ваш CSS Reset - это первое, что должен увидеть браузер

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

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

Некоторые могут задать логичный вопрос: почему так происходит? Ответ прост: правила, записанные ниже по тексту CSS-файла (и даже ниже по их порядку подключения в документе), перезаписывают правила, объявленные ранее.

Давайте не будем сильно отходить от темы и продолжим. Применим стили Эрика Мейера к нашему примеру, но после описания наших свойств, как показано в 4 примере . Математики бы сказали следующее: «Что и требовалось доказать».

3. Используйте отдельный CSS-документ для CSS Reset

Я должен (нет, меня отнюдь не вынудили) упомянуть этот совет. Использование отдельного файла для CSS Reset - это обычная практика, которую поддерживает большое число разработчиков.

На самом деле я придерживаюсь позиции создания одного большого CSS-файла из-за большей производительности подобного подхода. Но в данном вопросе я склонен согласиться с большинством: CSS Reset следует вынести в отдельный файл (обычно его называют reset.css). В таком случае вы можете использовать его в различных проектах, не прилагая при этом никаких усилий по его отделению от других правил CSS.

4. Старайтесь избегать использование универсального селектора

Несмотря на то, что эта концепция работает, её применение чаще всего не является желательным из-за несовместимости с некоторыми браузерами (например, данный селектор некорретно обрабатывается в Internet Explorer). Вам следует использовать этот приём только для простых, небольших, статичных и предсказуемых страниц (если уж вам пришлось делать это).

Данный совет особенно важен тогда, когда вы разрабатываете такие решения, как темы для CMS. Вы не можете заранее предсказать, как она будет использована и как её будут модифицировать. Лучше описать фундаментальные CSS-правила для всех элементов, чем использовать для этого непредсказуемый (пусть и меньший по объёму) механизм универсальных селекторов.

5. Избегайте избыточных описаний свойств при использовании CSS Reset

Ещё одна причина, по которой мне не нравится отдельный файл для CSS Reset - это потенциальная избыточность последующих деклараций CSS-свойств. Повторение отдельных ваших стилей среди всего набора CSS-файлов - это моветон и его следует избегать. Разумеется, иногда мы слишком ленивы, чтобы кропотливо пройтись по набору стилей и совместить некоторые из них, но следует хотя бы попытаться!

Вернёмся к CSS Reset от Эрика. Он устанавливает значения по умолчанию для line-height, color и background элемента body следующим образом:

body { line-height: 1; color: black; background: white; }

Допустим вы уже знаете, как будет выглядеть элемент body :
  1. background-color: #cccccc;
  2. color: #996633;
  3. Вы хотите по горизонтали повторять определённую фоновую картинку.

В этом случае нет необходимости создавать новый селектор для описания ваших свойств - вы можете их просто включить в CSS Reset. Сделаем это:

body { line-height: 1; color: #996633; background:#ccc url(tiled-image.gif) repeat-x top left; }

Не бойтесь модифицировать сам CSS Reset. Подстройте его под себя, заставьте его работать на себя. Изменяйте, перестраиваейте, убирайте и добавляйте так, как это нужно в вашем конкретном случае.

Эрик Мейер по этому поводу сказал следующее: «это не тот случай, когда всем следует использовать CSS Reset без изменений».