webpackとpugでJSキャッシュ対策をする

# はじめに
JSの修正を行ったのにブラウザのキャッシュでなかなか更新されない・・・
なんてことよくありますよね。
リリースしてしまった後の改修でそれが起こると、ブラウザのキャッシュがクリア
されることを祈るしかない状況に陥ったり。

そうなる前にキャッシュ対策をしてから開発を始めたいですね。
webpackとhtmlテンプレートエンジンである<a href="https://pugjs.org/api/getting-started.html" target="_blank">pug</a>と組み合わせれば
わりと簡単にキャッシュ対策ができます。

# pugとは

<a href="https://pugjs.org/api/getting-started.html" target="_blank">Getting Started – Pug</a>

htmlテンプレートエンジンの一種でhtmlを簡単に書くことができます。
閉じタグを書く必要がなくどこまでがタグなのかはインデントで判断されます。
そのためよくありがちなタグの閉じ忘れを防ぐことができます。
pugをコンパイルすることで通常のhtmlが出力されます。

```pug:index.pug
html(lang='ja')
    head
        meta(charset='UTF-8')
```
↓

```html:index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
  </head>
</html>
```

さらに変数、include、if、ループが使用できるため
ソースの二重管理等を減らすこともできてとても使い勝手が良いです。

# ターゲット
- pugを利用している方、もしくは採用を検討している方
- キャッシュに悩まされている方

# 前提条件
コンパイル環境については<a href="https://qiita.com/kawaii_komugi/items/b90f23e082b2c4df1879">前回の記事</a>を参照ください。
以降は前回との差異についてのみ記載していきます。

# 構成

```:構成図
├── src                    //ソース置き場
    ├── js
        ├── index.ts       //中身はなんでもOK
    ├── pug
        ├── index.pug
├── static                 //コンパイル後ファイル
    ├── js
        ├── index-[ハッシュ値].js
        ├── index-[ハッシュ値].js.map
        ├── manifest.json
    ├── pug
        ├── index.html
├── gulpfile.js
├── package.json
├── package-lock.json
├── webpack.config.js
```

構成はこのようになります。
> index-[ハッシュ値].js

このハッシュ値の部分が自動的に付与されてJSに変更があったときだけ
ファイル名が変わるため別ファイルと見なされてブラウザキャッシュがされなくなります。
この仕組みをまず実装しましょう。

# JSにハッシュ値を付与する

これは非常に簡単です。
webpack-manifest-pluginを使用するだけでOK。

```
npm install --save-dev html-webpack-plugin
```

webpack-manifest-pluginをインストールします。

```js:webpack.config.js
const {WebpackManifestPlugin} = require('webpack-manifest-plugin');

//中略

module.exports = {
  // エントリーポイント(メインのjsファイル)
  entry: entries,
  // ファイルの出力設定
  output: {
    path: jsDistPath,
    filename: '[name]-[hash].js'
  },
//中略
  plugins: [
    new WebpackManifestPlugin({
        publicPath: "",
        writeToFileEmit: true
    })
  ],

```

webpack-manifest-pluginを読み込んでpluginsで指定します。
そしてoutputのfilenameに-[hash]を指定してやります。
これでコンパイルするとmanifest.jsonが出力されます。

```
npx gulp js
```

```json:manifest.json
{
  "index.js": "index-6f1bee11ec9444bbed14.js",
  "index.js.map": "index-6f1bee11ec9444bbed14.js.map"
}
```

ハッシュ値を抜いたファイル名と実ファイル名の一覧が出来上がります。
これだけでも良いっちゃ良いんですが参照元のhtmlを手作業で変更するのは非常にめんどくさい。
pugを使用してやると、manifest.jsonを読み込んで実ファイル名を取得できるようになります。

# pugでjsonを読み込む

まず通常のpugコンパイルはこんな感じ。

```js:gulpfile.js
const config = {
  "baseDir": "src",
  "distDir": "static",
  "jsDir": "js",
  "pugDir": "pug"
};

//中略

gulp.task( 'pug', function() {
  return gulp
    .src([ paths.src+config.pugDir + '/*.pug', '!'+paths.src+config.pugDir + '/_*.pug' ])
    .pipe(plumber({
        errorHandler: notify.onError("Error: <%= error.message %>")
      }))
    .pipe(pug({
      pretty: true
    }))
    .pipe(rename({
        extname: '.html' 
    }))
    .pipe( gulp.dest( paths.dest+config.pugDir ) );
});
```

ここにmanifest.jsonを読み込む記述を追加します。

```js:gulpfile.js
var fs = require("fs");
var data = require("gulp-data");

//中略

gulp.task( 'pug', function() {
  return gulp
    .src([ paths.src+config.pugDir + '/*.pug', '!'+paths.src+config.pugDir + '/_*.pug' ])
    .pipe(data( file => {
        var json = {};
        json.manifest = JSON.parse(fs.readFileSync(paths.dest+config.jsDir+"/manifest.json"));
        return json;
    }))
```

読み込んだmanifest.jsonをjson.manifestにセットしてリターンしていますが
この"manifest"というkeyでpug上から参照することができます。

```pug:index.pug
html(lang='ja')
    head
        meta(charset='UTF-8')
        meta(name="viewport" content="width=device-width, initial-scale=1.0")
        meta(http-equiv="X-UA-Compatible" content="IE=edge")
        title jsCache
    body
        - var src = "../../static/js/" + manifest["index.js"]
        script(src=`${src}`)    
```

上で指定したkeyが変数名になります。（今回はmanifest）
後はこの変数を使ってscriptタグを作ればOKです。

# pugのコンパイル

manifest.jsonを更新しないとpugで読み込まれないので
JSのコンパイル→pugのコンパイルの順番で行います。

```
npx gulp js
npx gulp pug
```

```html:index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>jsCache</title>
  </head>
  <body>
    <script src="../../static/js/index-6f1bee11ec9444bbed14.js">   </script>
  </body>
</html>
```

scriptタグがハッシュ値付きのファイル名で作成されました！

# おわりに

webpack-manifest-pluginとpugを組み合わせる記事がなさそうだったので書いてみました。