Статьи

Используйте Gulp для связывания, минификации и кэширования

На прошлой неделе я обсуждал, как настроить Node.js и Gulp в Visual Studio 2015 . Во время этого обсуждения я упомянул, что я использую Gulp для объединения, минимизации и кеширования моих файлов HTML, CSS и JavaScript.

На этой неделе я собираюсь рассказать вам, как именно я это делаю.

Итак, если у вас еще не установлены Node.js и Gulp, вы можете вернуться и прочитать статью, которую я написал на прошлой неделе.

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

образ

Пакетирование

Первое, что мы хотим сделать, это объединить все наши файлы CSS и файлы JavaScript в один файл для CSS и один файл для JavaScript. Есть несколько способов, которыми вы могли бы сделать это, но я хотел, чтобы был какой-то способ, который позволил бы мне работать без привязки во время разработки и связываться, когда я выпускал код. Поскольку я работаю с одностраничным приложением (SPA), это было простой задачей: настроить файл по умолчанию на index.debug.html для разработки и index.release.html и использовать преобразования web.config, чтобы указать, какие из них должны быть используется в какой среде. Если вы работаете в какой-то другой среде, я уверен, что у вас есть какой-то аналогичный способ указания файла по умолчанию на основе настроек среды.

Итак, вся моя работа по разработке будет выполнена в index.debug.html.

Как я уже сказал, существует много способов объединения JavaScript и CSS-кода, но я обнаружил, что, как мне кажется, требуется наименьшее количество работы, это использование модуля gulp-useref . Этот модуль позволяет нам помещать токены в наш HTML-файл, которые указывают, какие файлы мы хотим сжать и каким должно быть полученное имя файла.

Ваш HTML-файл будет выглядеть примерно так:

<html>
<head>
  <!-- build:css css/combined.css -->
    <link href="css/one.css" rel="stylesheet">
    <link href="css/two.css" rel="stylesheet">
  <!-- endbuild -->
</head>
<body>
<!-- other normal content goes here -->
  <!-- build:js scripts/combined.js -->
    <script type="text/javascript" 
      src="scripts/one.js"></script> 
    <script type="text/javascript" 
      src="scripts/two.js"></script> 
  <!-- endbuild -->
</body>
</html>

Useref видит токен сборки и создает файл CSS с именем комбинированный.css и файл JavaScript с именем комбинированный.js и изменяет выходной HTML-код так, чтобы он выглядел следующим образом:

<html>
<head>
  <link rel="stylesheet" href="css/combined.css"/>
</head>
<body>
  <script src="scripts/combined.js"></script> 
</body>
</html>

Чтобы использовать эту функцию в своем файле Gulp, установите его с помощью NPM:

npm install gulp-useref --save-dev

Убедитесь, что вы запускаете NPM в каталоге, в котором находится файл gulpfile.js.

В своем файле Gulp вы добавите следующий код:

var gulp = require('gulp');
var useref = require('gulp-useref');

gulp.task('useRef', [],function() {
    return gulp.src('index.debug.html')
        .pipe(useref())
        .pipe(gulp.dest('index'));
});

Конечным результатом является то, что в подкаталоге с именем index появится новый файл index.debug.html вместе с новыми файлами css / комбинированный.css и scripts / комбинированный.js.

Условная обработка

Одна из многих вещей, которые мне нравятся в Gulp, это то, что он основан на потоке. То есть мне не нужно записывать файлы в каталог до тех пор, пока я не захочу — в отличие от Grunt (другого популярного инструмента для обработки файлов Node.js), где все целиком основано на файлах.

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

Для этого нам нужен gulp-if .

Вы можете установить gulp-if, используя NPM, используя следующую команду:

npm install gulp-if --save-dev

Основное использование gulp-if выглядит следующим образом:

var gulpif = require('gulp-if');

gulpif (/ * условие файла здесь * /,
/ * следующий процесс потока идет сюда * /);

Сократить JavaScript

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

Установите gulp-uglify, используя:

npm install gulp-uglify --save-dev

Итак, объединяя это с gulp-if, использование будет выглядеть так:

var gulpif = require('gulp-if');
var uglify = require('gulp-uglify');

... other code here

.pipe(gulpif('*.js', uglify()));

Это просто общая суть. Мы соберем все это вместе в нескольких параграфах.

Минимизировать CSS

Чтобы минимизировать CSS, я решил использовать gulp-cssnano .

Установите gulp-cssnano, используя:

npm install gulp-cssnano --save-dev

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

var cssnano = require('gulp-cssnano');
var gulpif = require('gulp-if');

... code here ...

.pipe(gulpif('*.css', uglify()));

Файлы карт

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

Чтобы создать файл карты, вам нужно использовать gulp-sourcemaps .

Установить с помощью:

npm install gulp-sourcemaps --save-dev

Я покажу вам, как это все подключается в ближайшее время.

Еще один пакет

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

Видите ли, чтобы заставить minify работать с useref, нам нужно установить модуль lazypipe . Который вы можете установить используя:

npm install lazypipe --save-dev

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

И вот теперь, наконец, мы можем собрать все это в один большой сценарий.

var gulp = require('gulp');
var useref = require('gulp-useref');
var uglify = require('gulp-uglify');
var cssnano = require('gulp-cssnano');
var lazypipe = require('lazypipe');
var sourcemaps = require('gulp-sourcemaps');
var gulpif = require('gulp-if');

// compressTasks is a sub process used by useRef (below) 
// that compresses (takes out white space etc) the 
// javascript and css files
var compressTasks = lazypipe()
    .pipe(sourcemaps.init, { loadMaps: true })
    .pipe(function () { return gulpif('*.js', uglify()); })
    .pipe(function() {
        return gulpif('*.css', cssnano({
                zindex: false }));
    });

// useRef looks at markers in index.debug.html and 
// combines all of the files into one file.  once the 
// files are combined the compressTasks process 
// is called and then the files are all written out to 
// the index directory.
gulp.task('useRef', [],function() {
    return gulp.src('index.debug.html')
        .pipe(useref({}, 
            lazypipe()
            .pipe(compressTasks)

            ))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest('index'));
});

Сжатие файла HTML

Итак, как говорится в комментариях, и, как я уже говорил, все это помещается в каталог index. Далее мы хотим сжать файл HTML и переместить index.debug.html обратно в корневой каталог с именем index.release.html, а затем поместить файл CSS и файл JavaScript в соответствующие каталоги, свисающие с корень.

Чтобы сжать HTML-файл, вы можете использовать модуль NPM gulp-htmlmin , который вы можете установить с помощью:

npm install gulp-htmlmin --save-dev

И, код:

var htmlmin = require('gulp-htmlmin');

// minIndex takes all of the whitespace out of the 
// main index file
gulp.task('minIndex', ['useRef'], function() {
    return gulp.src('index/index.debug.html')
        .pipe(htmlmin({ collapseWhitespace: true,
             removeComments: true }))
        .pipe(gulp.dest('index'));
});

Переименовать индексный файл

Для переименования файла мы используем gulp-rename

npm install gulp-rename --save-dev

И, следующий код:

var gulpRename = require('gulp-rename');

// renameIndex renames the index file and puts it
// in the root directory
gulp.task('renameIndex', ['minIndex'], function () {
    return gulp.src('index/index.debug.html')
        .pipe(gulpRename('index/index.release.html'))
        .pipe(gulp.dest('.'));
});

Это переименовывает файл и помещает его в корневой каталог.

Перемещение файлов

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

gulp.task('copyJs', ['useRef'], function () {
    // copy the js and map files generated from useref to 
    // the real app directory
    return gulp.src('index/app/*.*')
        .pipe(gulp.dest('app'));
});

gulp.task('copyCss', ['useRef'], function () {
    // copy the css and map files generated from useref to 
    // the real css directory
    return gulp.src('index/css/*.*')
        .pipe(gulp.dest('css'));
});

Очистки кэша

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

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

Чтобы реализовать очистку кеша, вам нужно установить gulp-cache-bust :

npm install gulp-cache-bust --save-dev

И последний кусочек кода, который заставит все это работать:

var cacheBuster = require('gulp-cache-bust');

// cacheBuster looks at the css and js files and appends a hash to the
// request to cause the file to get reloaded when the file changes.
gulp.task('cacheBuster', ['copyCss', 'copyJs', 'renameIndex'], function () {
    return gulp.src('index/index.release.html')
        .pipe(cacheBuster())
        .pipe(gulp.dest('.'));
});

Улучшения

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

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

Вы можете получить полный скрипт Gulp от GitHub .