- PVSM.RU - https://www.pvsm.ru -

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

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

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


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

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

Зайдем издалека. В экосистеме nodejs, существует такое понятие, как потоки [2], или 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 [3] — это как раз и есть реализация потоков, которые используют в 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
       * <File "index.js" <Buffer 2f 2a 20 67 6c 6f 62 ... >>
       * data callback
       * <File "another.js" <Buffer 2f 2a 2a 0a 20 2a 20 ... >>
       */
    })
    .pipe(gulp.dest('dist/'));
});

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

.pipe(gulp.dest('dist/'));

Осознание факта того, что потоки в gulp другие ведет к просветлению и пониманию, поскольку это объясняет большинство проблем и ошибок.

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

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

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

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

Как мы помним, содержимое файла в виниловском потоке может быть представлено в виде буфера или в виде потока данных. Однако не обязательно поддерживать и то другое. Всегда можно использовать пакет vinyl-buffer [8], который вычитает поток данных и сохранит его в буфер для последующих трансформаций.

Пока что все. Надеюсь, что вам стало немного понятнее, как работать с gulp. Спасибо.

Автор: Kukunin

Источник [9]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/270646

Ссылки в тексте:

[1] gulp: https://github.com/gulpjs/gulp

[2] потоки: https://nodejs.org/api/stream.html

[3] vinyl: https://github.com/gulpjs/vinyl

[4] gulp-browserify: https://www.npmjs.com/package/gulp-browserify

[5] видите: https://github.com/gulpjs/gulp/blob/master/docs/recipes/browserify-transforms.md

[6] vinyl-source-stream: https://github.com/hughsk/vinyl-source-stream

[7] данный шаблон: https://gist.github.com/Kukunin/4a13acf948019d05ae9a01777729230a

[8] vinyl-buffer: https://www.npmjs.com/package/vinyl-buffer

[9] Источник: https://habrahabr.ru/post/344626/?utm_campaign=344626