54
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

webpackでdigestをつけてキャッシュ対応 + digestされたファイルの参照

Last updated at Posted at 2017-09-25

はじめに

こちらの記事でwebpackでbundleされたファイルの管理を行いました。
ただこのままですと本番環境で実運用するにはファイルのキャッシュ対応が足りません。ブラウザがキャッシュをしてしまうので、ファイルにdigestをつけてデプロイのたびにファイル名が異なるようにしないといけないわけです。

ですので今回は、「digestつきのファイルを出力してそれを使ってみる」ということをやってみます。
※ jsやhtmlなどはこちらの記事のものを使います。

今回のゴール

  • webpackでbundleされたファイルにdigestをつける
  • digestつきのファイルを読み込み

digestつきのファイルを出力する

これはめちゃくちゃ簡単です。
webpack.config.jsでoutputの設定をしていると思いますが、出力ファイルのファイル名フォーマットに[hash]追加するだけです。

コードはこうなります。

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'Output Management'
    })
  ],
  output: {
    filename: '[name]-[hash].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

これを実行します。

$ yarn run build
yarn run v1.0.1
$ webpack
clean-webpack-plugin: /xxx/xxx/xxx/webpack-demo/dist has been removed.
Hash: e067b2f5e00633ee7372
Version: webpack 3.6.0
Time: 1130ms
                               Asset       Size  Chunks                    Chunk Names
  app-e067b2f5e00633ee7372.bundle.js     545 kB    0, 1  [emitted]  [big]  app
print-e067b2f5e00633ee7372.bundle.js    2.74 kB       1  [emitted]         print
                          index.html  296 bytes          [emitted]
   [0] ./src/print.js 84 bytes {0} {1} [built]
   [1] ./src/index.js 403 bytes {0} [built]
   [3] (webpack)/buildin/global.js 509 bytes {0} [built]
   [4] (webpack)/buildin/module.js 517 bytes {0} [built]
    + 1 hidden module
Child html-webpack-plugin for "index.html":
     1 asset
       [2] (webpack)/buildin/global.js 509 bytes {0} [built]
       [3] (webpack)/buildin/module.js 517 bytes {0} [built]
        + 2 hidden modules
✨  Done in 7.25s.

出力されたファイルを見てみます。

$ ls dist/
app-e067b2f5e00633ee7372.bundle.js	index.html				print-e067b2f5e00633ee7372.bundle.js

無事digestがついてました。
これだけです。簡単なお仕事でした。

html-webpack-pluginを使ってbundleしているので、dist/index.htmlではdigestつきのファイルが自動でセットされています。
dist/index.htmlを実際に見てみます。

dist/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Output Management</title>
  </head>
  <body>
  <script type="text/javascript" src="app-e067b2f5e00633ee7372.bundle.js"></script><script type="text/javascript" src="print-e067b2f5e00633ee7372.bundle.js"></script></body>
</html>

実際にブラウザで確認してみても問題なくbundleされたファイルが読み込まれていました。
これはindex.htmlから全出力ファイルを読み込むというケースですが、複数ファイルかつ特定ファイルしか読み込まないという設定もできますので次項でやっていきます。

複数ファイルかつ特定ファイルのみ読み込み

html-webpack-pluginにオプションを追加することでできます。オプション追加したコードは以下になります。

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'Output Management'
    }),
    new HtmlWebpackPlugin({
      filename: 'test.html',
      chunks: ['app']
    })
  ],
  output: {
    filename: '[name]-[hash].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

これをyarn run buildでbundleしてみます。まず出力されたファイルを確認します。

$ ls dist/
app-7f4dcc4aa728d3280cbc.bundle.js	index.html				print-7f4dcc4aa728d3280cbc.bundle.js	test.html

test.htmlが出力されていればオーケーです。次にtest.htmlの中身を見てみます。

dist/test.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  </head>
  <body>
  <script type="text/javascript" src="app-7f4dcc4aa728d3280cbc.bundle.js"></script></body>
</html>

digestされたapp.jsだけ読み込まれていることがわかります。

[番外編] Railsからjsファイルを参照する場合

digestがついているのでちょっと厄介です。bundleのたびに参照箇所を変更するわけにもいきません。そこで・・・webpack-manifest-pluginを使います。

このプラグインはbundle前のファイルとbundle後のファイルをマッピングしてくれます。
マッピングされたデータを元に読み込むようにすることで、digestが変わっても変更の必要がないというわけです。

ただ、このマッピングされたデータを元に読み込むためには少し工夫が必要です。Railsを使ってサンプル作って見ます。

まずは、webpack.config.jsの設定を変更します。

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');

module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'Output Management'
    }),
    new HtmlWebpackPlugin({
      filename: 'test.html',
      chunks: ['app']
    }),
    new ManifestPlugin()
  ],
  output: {
    filename: '[name]-[hash].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

これをいつも通りyarn run buildでbundleしてみます。出力結果は、

$ ls dist/
app-7f4dcc4aa728d3280cbc.bundle.js	index.html				manifest.json				print-7f4dcc4aa728d3280cbc.bundle.js	test.html

manifest.jsonというファイルが作成されていることがわかります。中身を見てましょう。

manifest.json
{
  "app.js": "app-7f4dcc4aa728d3280cbc.bundle.js",
  "print.js": "print-7f4dcc4aa728d3280cbc.bundle.js"
}

マッピングされていることがわかります。
Railsのviewでdigestされたjsファイルを読み込む場合、このmanifest.jsonのデータを使って読み込めばオーケーです。
検索すればいろいろ落ちていますが、以下簡単なサンプルになります。

前提条件として、manifest.jsonはRailsルート直下のpublic/assets下とします。

※動作確認はしていませんごめんなさいm(__)m

まずは、起動時にmanifestファイルを読み込んでマッピングデータをパースする処理を追加します。

config/initializers/assets_manifest.rb
Rails.application.config.assets_manifest =
  if File.exist?(Rails.root.join('public', 'assets', 'manifest.json'))
    JSON.parse(File.read(Rails.root.join('public', 'assets', 'manifest.json')))
  end

次にviewでjsファイルを参照する際に、マッピングデータを元に参照されるようにします。
application_helper.rbに関数を用意しましょう。

app/helpers/application_helper.rb
module ApplicationHelper
  def assets_path(name)
    return "/assets/#{path}" if Rails.env == 'local'
    manifest = Rails.application.config.assets_manifest
    file_path = manifest && manifest[name].present? ? manifest[name] : name
    "/assets/#{file_path}"
  end
end

ローカル環境ではdigest化していない場合を想定してみました。
実際にjsファイルを読み込んでいるところはこうなります。

app/views/layouts/application.html.erb
<script type="text/javascript" src="<%= assets_path('app.js') %>"></script>

これで以上になります。

おわりに

webpackで簡単にdigest化できることが分かりました。html上で自動で毎回変更されるハッシュ値に対応させるのも簡単にできました。
Railsで対応させるのがちょっと面倒くさって思ってしまいました。もっといいやり方があったら教えてくださいm(__)m

54
47
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?