チェ・ゲバムラの日記

脱犬の道を目指す男のブログ

【タスクランナー】もうキャッシュのせいなんて言わせない。CSS・JavaScript・画像・PDFなど全ての読み込みファイルにパラメータをつけてファイル出力する方法

前回の記事でタスクランナーのススメを書いたが、少しアップデートしたので紹介する。

前回のタスクランナーの記事
hiromode.hatenablog.com


内容としては
・PUG削除
・HTMLのパラメータ付与の上出力
を追加した。
理由はPUGの書き方を習得するのが面倒だし、
HTMLキャッシュを防ぐためにパラメータを付与するためということに尽きる。

コピペでできるように順番にコードを記載する。

今回の環境

MacOS内にvirtualbox,CentOS7をインストール済み。
またnode.jsはインストールされているものとする。

前提条件

キャッシュさせたくないところ(パラメータにするところ)に__NOCACHE__と記載する。
別に他の文字列でもいいが、変える場合はindex.html内と併せて必ず後述するgulpfile.js内の文字列も変更すること。

一例としてindex.htmlを記載しておく

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>テンプレート</title>
    <link rel="stylesheet" href="css/style.css?__NOCACHE__">
    <script src="js/app.js?__NOCACHE__"></script>
  </head>
  <body>
    <header>
    index
    </header>
    <img src="img/a.png?__NOCACHE__">
  </body>
</html>

モジュールをインストール

エラー通知
npm install gulp-notify --save-dev

エラーでも監視継続
npm install gulp-plumber--save-dev

SassをCSSコンパイル
npm install gulp-sass --save-dev

ベンダープレフィックス付与
npm install gulp-autoplefixer --save-dev

JS圧縮
npm install gulp-uglify --save-dev

PugをHTML変換
npm install gulp-pug --save-dev


変更したファイルのみ検知
npm install gulp-changed-in-place --save-dev


変更したファイルのみ検知
npm install gulp-replace --save-dev

赤が前回記事からアップロードした分



モジュールをインストール成功すると下記のようなファイルができている。
(※下記は一例であり、インストールしたものによって変わる。一度できたらpackage.jsonはコピーして使える)

package.json

{
  "name": "sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel": "^6.23.0",
    "babel-preset-es2015": "^6.24.1",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^6.0.0",
    "gulp-changed-in-place": "^2.3.0",
    "gulp-if": "^2.0.2",
    "gulp-notify": "^3.2.0",
    "gulp-plumber": "^1.2.0",
    "gulp-pug": "^4.0.1",
    "gulp-replace": "^1.0.0",
    "gulp-sass": "^4.0.2",
    "gulp-uglify": "^3.0.1",
    "gulp-webserver": "^0.9.1"
  },
  "babel": {
    "presets": [
      "es2015"
    ]
  }
}

下記をインストールディレクトリに作成。

gulpfile.js

// gulpプラグインの読み込み

var gulp = require('gulp');
var notify = require("gulp-notify"); // エラー時の通知
var plumber = require('gulp-plumber'); // エラーでもファイル監視(watch)を続ける
var sass = require("gulp-sass"); //SassをCSSに
var autoprefixer = require("gulp-autoprefixer"); //Sassにベンダープレフィックスをつける
var uglify = require('gulp-uglify'); //JSの圧縮
var replace = require('gulp-replace');
var changedInPlace = require('gulp-changed-in-place');//変更した場合のみ処理

//var browserSync = require('browser-sync').create()
//var babelify = require('babelify');
//var browserify = require('browserify');
//var buffer = require('vinyl-buffer');
//var source = require('vinyl-source-stream');
//var sourcemaps = require('gulp-sourcemaps');
//var uglify = require('gulp-uglify');
//var destDir = './public'  // 出力先
//
//
////ES6変換
//gulp.task('es6', () => {
//  browserify({entries: 'src/main.js', debug: true})
//    .transform(babelify)
//    .bundle()
//    .on('error', err => console.log('Error : ' + err.message))
//    .pipe(source('main.js'))
//    .pipe(buffer())
//    .pipe(sourcemaps.init({loadMaps: true}))
//    .pipe(uglify())
//    .pipe(sourcemaps.write('./'))
//    .pipe(gulp.dest(destDir))
//    .pipe(browserSync.reload({stream: true}))
//})

//scssにベンダープレフィックスをつけて圧縮してコンパイル
gulp.task('scss', function() {
    gulp.src('./scss/**/*.scss')
        .pipe(plumber())
        .pipe(autoprefixer({ // ベンダープレフィックスの付与
            browsers: ['last 3 version', 'ie >= 10','iOS >= 9.3', 'Android >= 4.4'], // (ベンダープレフィックスを付与する)対応ブラウザの指定
            cascade: false // 整形しない
        }))
        .pipe(sass({outputStyle: 'expanded'}))
        .pipe(gulp.dest('./css')); // cssディレクトリに書き出す
});

//jsを圧縮
gulp.task('js', function() {
    //実行するファイル
    gulp.src('./js/**.js')
        //pipeでつなぐ
        .pipe(plumber())//エラーでもファイル監視継続watch
        .pipe(uglify())// 圧縮
        .pipe(gulp.dest('./js/min'));//圧縮したファイルを出力
});


//HTMLファイルのビルド(タイムスタンプをパラメーターとして付与、置換する)
gulp.task('build', function() {
    ts = Date.now();
    //実行するファイル
    gulp.src(['./**/*.html','!./build/*'])
        //pipeでつなぐ
        .pipe(plumber())//エラーでもファイル監視継続watch
        .pipe(changedInPlace())
        .pipe(replace(/__NOCACHE__/g, ts))//置換
        .pipe(gulp.dest('./build'));//ディレクトリに書き出す
});


// 自動監視化
gulp.task('watch', function(){
    gulp.watch('./scss/**/*.scss',{interval: 500}, ['scss']);
    gulp.watch('./js/**/*.js', {interval: 500}, ['js']);
    gulp.watch('./**/*.htm*', {interval: 500}, ['build']);
});

gulp.task('default', ['watch']);

実行

npx gulp


あとはbuildフォルダができて、更新するたびに新しいパラメータに置換されて
フォルダ階層も保ったままindex.htmlなどが出力されるので、それを公開サーバーなどにアップロードしたり納品したりする。


2019/4/18追記
バージョンを上げてしまうと上記gulpfile.jsの記述がエラーになってしまう様子。
その場合は下記のように変更すると動く。

// キャッシュパラメータを付与
gulp.task('build', function () {
    ts = Date.now();
    return gulp.src(['./**/*.html', '!./build/*'])
        .pipe(plumber())
        .pipe(changedInPlace())
        .pipe(replace(/__NOCACHE__/g, ts))
        .pipe(gulp.dest('./build'));
});

// 自動監視化
gulp.task('watch', function () {
    gulp.watch('./**/*.htm*', { interval: 500 }, gulp.task('build'));
});

gulp.task('default', gulp.task('watch'));



P.S.
ちなみにPHPの場合は簡単で、下記のようにファイルの更新日時をとってくるようにすれば良い。
src="$ファイルパス?ver="

//記述例
<img src="/img/example.png?ver=<?php echo filemtime($_SERVER['DOCUMENT_ROOT'].'/img/example.png'); ?>">

・既存のPHPを一括置換で対応させる場合

//検索:
src="((.*)png)"
//置換:
src="$1?ver=<?php echo filemtime($_SERVER['DOCUMENT_ROOT'].'$1'); ?>"

注意点:一行に複数該当箇所があると正しく置換できない。


・既存のWordpressの場合

//検索:
src=".*?>((.*)png)"
//置換:
src="<?php echo get_template_directory_uri(); ?>$1?ver=<?php echo filemtime($_SERVER['DOCUMENT_ROOT'].'/wp/wp-content/themes/xxxxxxxxxxxxx$1'); ?>"