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

    Всем привет. Если вы связаны хоть как-то с 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
           * <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. Но видите приписку, которая говорит, что плагин deprecated, т.е. Устарел.



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

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

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

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

    Ну. И что?
    Реклама
    Комментарии 18
    • 0
      Gulp это ведь таск раннер, его не только для сборки можно использовать. В целом информация уже не слишком актуальна, тк существуют более продвинутые решения для сборки если речь идет о вебе (но как таск раннер gulp все еще может быть актуален). Тоже самое касается упомянутого browserify. То есть года 3 назад подобный материал был бы более актуален, сейчас время можно потратить на изучение чего-то более современного. В целом вот здесь можно полазить кто не вкурсе трендов stateofjs.com/2017/introduction
      • –1
        Подтверждаю
        • +3
          Что сейчас предлагает использовать?
          • 0
            Выше указана ссылка, там все основное перечислено (не пропускаем подраздел Other Answers).
            • 0
              наводящий вопрос — для чего использовать? если того же гальпа хватает для повседневных задач — зачем использовать что-то еще? сейчас еще webpack довольно популярен, вроде как умеет помимо сборки еще и как таскраннер использоваться.

              кстати, я не призываю каждый год переходить на новые фреймворки, либы и бандлеры, но гуглить что-то в духе «чего используют в нынешнем году» и «gulp vs webpack» (подставьте свое) — полезно даже чисто для ознакомления.

              p.s. gulp устарел хотя бы в том плане, что еще пару лет назад о нем не рассказал только ленивый.
          • –2

            Не морочьте людям голову, Гульп, браузерифай, и иже с ними устарели так уже с года 2014. Сейчас в теме Webpack

            • 0
              Да ну, webpack не заменяет gulp.
              • +2

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

              • +1
                Каждому свое. Вот по тому же stateofjs, gulp не сильно и устарел:



                К тому же, это разные инструменты и я не спорю, чтоб для обычной сборки JS webpack предпочтительней, но если кастом, то не нужно gulp списывать со счетов чисто из-за того, что ему больше трех лет.
                • +2
                  Опишите кейс, где лучше сделать сборку на gulp, чем на webpack + npm?
              • 0

                Привет из 2014

                • +1
                  Привет. У нас биток тут по $300 =)
                • +2

                  Киллер фича gulp это pipelines. Unix way как он сеть. И да мне не нужен вебпак для прототипа в 100кб cорцов.

                  • 0

                    Нужность вебпака не зависит от числа килобайт.


                    Если у вас больше чем один js файл в проекте, то gulp.src склеит все файлы по алфавиту, а не в порядке зависимостей друг от друга, как это делает webpack

                  • 0
                    Уже год с лишним как отказался от использования гульпа, а тут кто-то только проснулся…
                    • 0
                      Не все такие, как вы
                      • 0
                        по какой причине?
                        • +1
                          Слишком замедляет сборку. Использую просто cli-тулы и запускаю из npm: npm run…

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

                      Самое читаемое