Vueアプリケーションデプロイ後にクライアントで自動リロードする方法

674
NO IMAGE

Webアプリケーションをデプロイした後、クライアント側でアップデートを検知して再読み込みをさせたい場合、ファイルの変更を検知して再読み込みするスクリプトを作るのが一般的と思われる。

ではVueの場合どうすればできるか調べたが、HtmlWebpackPluginを使ってカスタムのテンプレートに再読み込み処理を書くのが簡単なようだ。またアプリケーションの変更の検知については、生成されるファイルにハッシュ値を付加するのがもっとも簡単だろう。(デフォルトの動作がこれ。)

以下はvue.config.jsの例。

const HtmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  configureWebpack: {
    plugins: [
      new HtmlWebpackPlugin({
        template: 'template/index.html'
      })
    ]
  }
}

ここから読み込まれるテンプレートは、以下のように記述する。自動更新処理は、デプロイ時には古いJSファイルは削除し、新しいファイルだけが存在するという前提の元、2分に1回ファイルにアクセスしてレスポンスが200番台でなければリロードという処理にしている。(※このままの実装だと強制再読み込みになってそれまで操作してきたコンテキストが失われてしまうので、確認ダイアログを出すなどした方がベター。)

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <title>Vue App</title>
  </head>
  <body>
    <div id="app"></div>
    <script lang="javascript">
        var current = ""
        current ="<%= htmlWebpackPlugin.files.chunks["app"].entry %>"
        function checkVersion (url) {
            console.log('checking new version')
            fetch(url).then(function(response) {
              if (!response.ok) {
                console.log('detected new version')
                window.location.reload()
              }
            }).catch(function(reason) {
            });
        }
        function initInterval (url, frequency = 1000 * 60 * 2) {
            setInterval(() => {
            checkVersion(url);
            }, frequency);
        }
        initInterval(currentHash)
    </script>
  </body>
</html>

テンプレートのディレクティブ部分ではビルドで生成されるJSファイル群であるhtmlWebpackPlugin.files.chunksから、アプリケーションのエントリポイントを取得している。なお特に他に何も書かなくとも、Vueが生成するJSの読み込みなどは勝手に追加してくれるようだ。

目次

追記

@vue/cli-service 5.0.8, html-webpack-plugin 5.5.3では上記の記述方法ではエラーになる。まずvue.config.jsでHtmlWebpackPluginをpluinsに追加してindex.htmlを生成しようとすると、ファイルの重複でエラーになった。そのため、下記のようにchainWebpackでhtml-webpack-pluginのテンプレートを置き換えて対応した。


module.exports = {
  chainWebpack: (config) => {
    config
      .plugin('html')
      .tap(args => {
          args[0].template = 'template/index.html'
          return args;
      })
  }
}

またテンプレートではこれまでのhtmlWebpackPlugin.files.chuksではなく、htmlWebpackPlugin.files.jsを参照するようにする。

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Vue App</title>
  </head>
  <body>
    <div id="app"></div>
    <script lang="javascript">
        var currentHash = "<%= htmlWebpackPlugin.files.js[0] %>"
        function checkVersion (url) {
            console.log('checking new version')
            fetch(url).then(function(response) {
              if (!response.ok) {
                console.log('detected new version')
                window.location.reload()
              }
            }).catch(function(reason) {
            });
        }
        function initInterval (url, frequency = 1000 * 60 * 2) {
            setInterval(() => {
            checkVersion(url);
            }, frequency);
        }
        initInterval(currentHash)
    </script>
  </body>
</html>