Версионность ассетов при использовании gulp
При изменении клиентского кода нужно как-то заставить браузер клиента перезагрузить измененные файлы. Эта проблема достаточно распространенная. Скорее всего, вы столкнетесь с ней вне зависимости от того, какими технологиями пользуетесь при веб-разработке. Этот пример о решении проблемы при создании фронтенд проекта с использованием javascript и gulp.
Решить можно несколькими способами.
Пусть, например, 7e50961489 - это новая версия ассетов. На самом деле, она может быть любой, главное, чтобы отличалась от тех, что были раньше. Можно просто нумеровать - 1, 2, 3 и т.д. Но так не слишком удобно. Дальше станет понятно, почему.
1) Можно при обновлении приложения всегда менять имена файлов:
<script src="/js/lib-7e50961489.js" type="text/javascript"></script>
2) Можно добавлять версию ассетов, например:
<script src="/js/lib.js?v=7e50961489" type="text/javascript"></script>
3) Можно менять путь к файлу:
<script src="/js/7e50961489/lib.js" type="text/javascript"></script>
Последний вариант хорош тем, что не нужно менять имя файла, а фрагмент пути можно разрулить через конфигурацию nginx. (Таким образом можно не менять файловую структуру).
В этом примере рассмотрим первый вариант.
В одной из предыдущих заметок мы разобрали пример конфигурации gulp-файла для фронтенд проекта. Если раньше вы не работали с gulp, обязательно ознакомьтесь с ней перед дальнейшем чтением.
Для того, чтобы решить поставленную вначале задачу, нам понадобится еще три плагины для gulp: gulp-rev, gulp-rev-replace, gulp-rename:
npm install gulp-rev gulp-rev-replace gulp-rename --save
Основную работу делает gulp-rev. В его задачу входит переименование файлов ассетов в соответствии с версией.
Как и обещал, объясняю, почему версия не просто 1, 2, 3, ... Версия содержит информацию о контрольной сумме файлов. Поэтому файлы без изменений не пересобираются.
Итак, расширим задачу билда CoffeeScript-файлов. Для начала, используем gulp-rev:
gulp.task('coffee-lib', function () {
gulp.src(['./js/**/*.coffee', '!./js/app/web.coffee'])
.pipe(coffee())
.pipe(concat('lib.js'))
// добавляем версию в имя файла
.pipe(rev())
.pipe(gulp.dest('./public/js/'))
// сохраняем версию в файл - понадобится позже
.pipe(rev.manifest('rev-manifest-lib.json'))
.pipe(gulp.dest('./build'))
;
});
С самими файлами ассетов разобрались. Дальше нужно, чтобы в местах подключения были задействованы новые файлы. Для этого я создал "шаблонный файл" index.html.dist, который при каждой сборке будет билдится в index.html, причем последний должен быть не под контролем версий.
<!--index.html.dist-->
....
<script src="js/lib.js"></script>
....
Используем rev-replace для замены путей к подключаемым файлам и rename, чтобы переименовать (точнее, скопировать) index.html.dist в index.html:
gulp.task('template', ['coffee-lib'], function () {
var manifest = gulp.src([
'./build/rev-manifest-lib.json',
]);
return gulp.src('./public/index.html.dist')
.pipe(revReplace({
manifest: manifest,
replaceInExtensions: ['.dist']
}))
// переименуем файл
.pipe(rename("index.html"))
.pipe(gulp.dest('./public'))
;
});
Обратите внимание на строку в конфиге replaceInExtensions: ['.dist']. По умолчанию, rev-replace не обрабатывает .dist-файлы
Осталось только добавить новую('template') задачу в 'default' task ('coffee-lib' уже должна была быть добавлена раньше):
gulp.task('default', [..., 'template']);