gulp でJS, CSS, イメージファイルのキャッシュ問題を回避する


# キャッシュ問題について

Web ページの高速化のため、キャッシュを利用して高速化等を行ったりしますが、公開した JS,CSS 等に問題があったときに、それらを修正し、
即座に反映させたいがキャッシュにより、それが反映されないといったことが度々起こります。

これは、キャッシュにより、改めてデプロイした最新の CSS,JS 等が読み込まれ無いことで起こっている現象で、
これを回避するために、Rails であれば Asset Pipeline という機能があり、具体的には読み込む JS, CSS のファイル名にハッシュ値を追加し、デプロイ時にそれを実行することで、読み込む JS, CSS を制御しています。

今回はこれを gulp の task で実現したいと思います。


# 使用する node_module

- [gulp-rev](https://github.com/sindresorhus/gulp-rev)  
各ファイルに対して、ファイル名にハッシュ値を付与するモジュール  
変換したファイル名の manifest ファイルの吐き出しまで行う  
例) app.js -> app-ffd7597286.js  
- [gulp-rev-replace](https://github.com/jamesknelson/gulp-rev-replace)  
gulp-rev で吐き出した manifest ファイルを元に、記載されている対応によって、ファイル名を書き換えてくれるモジュール

# ディレクトリ / ファイル構成
```
src/
 |-- images/
 |    |-- favicon.ico
 |-- javascripts/
 |    |-- app.coffee
 |-- stylesheets/
 |    |-- app.sass
 |-- views/
     |-- index.jade
gulpfile.coffee
node_modules/
pakage.json
public/    # build 後のディレクトリ
 |-- assets/
 |-- index.html
```
# 実際のコード

gulpfile(CoffeeScript) に下記コードを記載します。

```:gulpfile.coffee
gulp         = require 'gulp'

del          = require 'del'
sequence     = require 'run-sequence'

coffee       = require 'gulp-coffee'
jade         = require 'gulp-jade'
rev          = require 'gulp-rev'
revReplace   = require 'gulp-rev-replace'
sass         = require 'gulp-sass'

basePath = 'src/'
baseDestPath = 'public/'
paths =
  html:
    src:  basePath     + 'views/*.jade'
    dest: baseDestPath
  js:
    src:  basePath     + 'javascripts/**/*.coffee'
    dest: baseDestPath + 'assets/'
  css:
    src:  basePath     + 'stylesheets/**/*.sass'
    dest: baseDestPath + 'assets/'
  img:
    src:  basePath     + 'images/**/*'
    dest: baseDestPath + 'assets/'
  rev:
    src:  baseDestPath + 'assets/**/*.+(js|css|png|gif|jpg|jpeg|svg|woff|ico)'
    dest: baseDestPath + 'assets/'
    manifestFileName: 'hoge-manifest.json'
  rev_replace:
    src:  baseDestPath + '**/*.+(html|css|js)'
    dest: baseDestPath


gulp.task 'default', ['build']

gulp.task 'build', (cb) ->
  sequence 'clean', 'coffee', 'sass', 'image', 'jade', 'rev', 'rev:replace', cb

gulp.task 'clean', (cb) ->
  del(["#{baseDestPath}/*"], cb)

gulp.task 'coffee', (f) =>
  gulp.src paths.js.src
    .pipe coffee()
    .pipe gulp.dest paths.js.dest

gulp.task 'jade', (f) ->
  gulp.src paths.html.src
    .pipe jade()
    .pipe gulp.dest paths.html.dest

gulp.task 'image', (f) ->
  gulp.src paths.img.src
    .pipe gulp.dest paths.img.dest

gulp.task 'sass', (f) ->
  gulp.src paths.css.src
    .pipe sass()
    .pipe gulp.dest paths.css.dest

gulp.task 'rev', (f) ->
  gulp.src paths.rev.src                                             # (2)
    .pipe rev()                                                      # (3)
    .pipe gulp.dest paths.rev.dest                                   # (4)
    .pipe rev.manifest(paths.rev.manifestFileName)                   # (5)
    .pipe gulp.dest paths.rev.dest                                   # (6)

gulp.task 'rev:replace', (f) ->
  manifest = gulp.src paths.rev.manifestFileName                     # (7)
  gulp.src paths.rev_replace.src                                     # (8)
    .pipe revReplace manifest: manifest                              # (9)
    .pipe gulp.dest paths.rev_replace.dest                           # (10)
```

### コードの説明

- (1)  
使用するモジュールの読み込み  
- (2)  
対象のファイルを記載
js,cs 等、キャッシュされたくないファイルを指定
- (3)  
`rev()` でファイル名にハッシュ値を付与
- (4)  
ハッシュ値を付与したファイルの出力先を指定
- (5)  
該当のファイル名とハッシュ値を付与したファイル名の対応を JSON で生成。  
標準で出力されるファイル名は、「rev-manifest.json」で、(3) の `rev()` の第一引数にファイル名を指定することで  
任意の名前で生成することが可能( `rev('hoge-manifest.json')` )

```:hoge-manifest.json
{
  "app.css": "app-d41d8cd98f.css",
  "app.js": "app-34208c3063.js",
  "favicon.ico": "favicon-58c242b34b.ico"
}
```

- (6)  
(5) で生成した manifest ファイルの出力先を指定  
- (7)  
manifest ファイルの読込  
- (8)  
書き換え対象のファイル名を指定  
- (9)  
manifest ファイルを指定して、対象のファイル内を書き換え  
- (10)  
書き換え後のファイルの出力先を指定

# 実行後のソース

```jade:index.jade
doctype html
html
  head
    meta(charset="utf-8")
    title gulp-rev-sample
    link(rel='shortcut icon', href='assets/favicon.ico')
    link(rel='stylesheet', href='assets/app.css')
  body

    script(src='assets/app.js')
```

```:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>gulp-rev-sample</title>
    <link rel="shortcut icon" href="assets/favicon-58c242b34b.ico">
    <link rel="stylesheet" href="./assets/app-d41d8cd98f.css"/>
  </head>
  <body>

    <script src="./assets/app-34208c3063.js"></script>
  </body>
</html>
```

**これで、もうキャッシュは怖くない！！**

サンプルは下記に置いてます。

[yutackall/gulp-rev-sample](https://github.com/yutackall/gulp-rev-sample)