部署后如何强制客户端重新加载?

时间:2015-03-27 01:56:25

标签: javascript node.js socket.io client mean-stack

我正在使用MEAN堆栈(mongo,express,angular和node)。我每隔几天就会相对频繁地部署到生产中。我担心的是我有时会更改客户端代码和API,我宁愿不必确保API与以前版本的客户端代码的向后兼容性。

在这种情况下,当我推向生产时,确保所有客户重新加载的最有效方法是什么?我已经看过,例如Evernote有一个弹出窗口,上面写着一些内容,请重新加载浏览器以获取最新版本的Evernote。我想做类似的事情...我需要沿着socket.io或sock.js的路径走下去,还是我错过了一些简单的东西,有一种更简单的方法来实现这个目标?

6 个答案:

答案 0 :(得分:7)

<强>更新 AppCache于2015年夏季被弃用,因此以下不再是最佳解决方案。新建议是使用Service Workers代替。但是,服务工作者目前仍然在IE和Safari中使用粗略的(读取:可能没有)支持。

或者,许多构建工具现在无缝地结合缓存清除和文件“版本控制”技术来解决OP问题。 WebPack可以说是这个领域的现任领导者。


这可能是使用HTML5的AppCache

的一个很好的用例

您可能希望将部分步骤自动化到部署脚本中,但这里有一些您可能觉得有用的代码可以帮助您入门。

首先,创建您的appcache清单文件。这也允许您在客户端浏览器中缓存资源,直到您明确修改appcache清单文件的日期为止。

/app.appcache:

CACHE MANIFEST

#v20150327.114142

CACHE:
/appcache.js
/an/image.jpg
/a/javascript/file.js
http://some.resource.com/a/css/file.css

NETWORK:
*
/

app.appcache 中,行#v20150327.114142上的注释是我们向浏览器指示清单已更改并应重新加载资源的方式。它可以是任何东西,只要文件看起来与以前版本的浏览器不同。在应用程序中部署新代码期间,应修改此行。也可以使用构建ID。

其次,在您要使用appcache的任何页面上,修改标头标签:

<html manifest="/app.appcache"></html>

最后,您需要添加一些Javascript来检查appcache是​​否有任何更改,如果有,请执行相关操作。这是一个Angular module。对于这个答案,这是一个香草的例子:

appcache.js:

window.applicationCache.addEventListener('updateready', function(e) {
    if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
        // Browser downloaded a new app cache.
        // Swap it in and reload the page to get the latest hotness.
        window.applicationCache.swapCache();
        if (confirm('A new version of the application is available. Would you like to load it?')) {
            window.location.reload();
        }
    }
    else {
        // Manifest didn't changed. Don't do anything.
    }
}, false);

或者,如果AppCache不适合您的情况,更多的贫民窟解决方案是创建一个简单的API端点,返回当前构建ID或上次部署日期时间。您的Angular应用程序偶尔会点击此端点并将结果与​​其内部版本进行比较,如果不同,则重新加载。

或者,您可以考虑使用实时重新加载脚本(example),但是,虽然在开发中非常有用,但我不确定使用实时/就地重新加载是多么好的想法生产中的资产。

答案 1 :(得分:1)

也许您可以在客户端代码文件名中添加哈希值。例如app-abcd23.js
因此浏览器将重新加载文件而不是从缓存中获取它。或者您可以将哈希添加到url.eg app.js?hash=abcd23,但某些浏览器仍可能使用缓存版本。

我知道rails有资产管道处理它,但我不熟悉MEAN stack。为此目的,npm中应该有一些包。

如果您想通知用户他们的客户端代码已过期,我认为没有必要使用socket.io。您可以在html meta tagjs文件中定义您的版本,如果不匹配,则显示弹出窗口并告诉用户刷新。

答案 2 :(得分:1)

我会先告诉你我的问题,然后我会推荐一个暂定的解决方案。我想强制我的用户注销,然后在部署生产版本时登录。在任何时候,生产中都会部署两个版本的软件。 FE 知道的软件版本和 Backend 知道的版本。大多数情况下,它们是相同的。在任何时候,如果它们不同步,那么我们需要重新加载客户端,让客户端知道已经推送了新的生产版本。

我假设后端有 99.99% 的时间了解生产中已部署软件的最新版本。

以下是我想推荐的两种方法:-

  1. 后端 API 应始终在响应标头中返回软件的最新版本。在前端,我们应该有一段通用的代码来检查 API 返回的版本和 FE 上显示的版本是否相同。如果没有,则重新加载。

  2. 每当用户登录时,BE 应在 JWT 中对最新的软件版本进行编码。并且 FE 应该继续将其作为不记名令牌与每个 API 请求一起发送。 BE 还应该为每个 API 请求编写一个通用拦截器。这将比较从 API 请求收到的 JWT 中的软件版本和

答案 3 :(得分:0)

  1. 尝试限制您的js /文件在较短的周期时间内过期,即:1天。
  2. 但是如果您想要弹出一些东西并告诉您的用户重新加载( ctrl + f5 )他们的浏览器,那么只需创建一个弹出该新闻的脚本如果您刚刚更改了一些文件,请标记刚刚重新加载/告知重新加载的ip / session,这样他们就不会被多个弹出窗口烦恼。

答案 4 :(得分:0)

我最近遇到了同样的问题。我通过使用我的js / css文件附加我的应用程序的内部版本号来修复此问题。我的所有脚本和样式标签都包含在一个公共包含文件中的脚本中,因此在js / css文件路径的末尾添加“内部版本号”是很简单的

/foo/bar/main.js?123

这是我在同一个头文件中跟踪的数字。每当我希望客户端强制下载应用程序的所有js文件时,我都会增加它。这使我可以控制何时下载新版本,但仍允许浏览器在第一个请求之后为每个请求利用缓存。直到我通过增加内部版本号来推送另一个更新。

这也意味着我可以拥有一个我想要的缓存过期标题。

答案 5 :(得分:0)

在构建过程中为本地存储设置唯一密钥 我正在使用react static并加载我自己的数据文件,每次内容更改时我都会在其中设置ID

然后,前端客户端从本地存储读取密钥 (如果密钥不存在,则必须是浏览器的首次访问) 如果本地存储中的密钥不匹配,则表示内容已更改 下方的火线强制重新加载

window.replace(window.location.href + '?' + key)

在我的情况下,我不得不再次运行同一行 喜欢

setTimeout( (window.replace(window.location.href + '?' + key))=> {} , 1000)

下面的完整代码:

const reloadIfFilesChanged = (cnt: number = 0, manifest: IManifest) => {
    try {
        // will fail if window does not exist
        if (cnt > 10) {
            return;
        }
        const id = localStorage.getItem('id');
        if (!id) {
            localStorage.setItem('id', manifest.id);
        } else {
            if (id !== manifest.id) {
                // manifest has changed fire reload
                // and set new id
                localStorage.setItem('id', manifest.id);
                location.replace(window.location.href + '?' + manifest.id);
                setTimeout(() => {
                    location.replace(window.location.href + '?' + manifest.id + '1');
                }, 1000);
            }
        }
    } catch (e) {
        // tslint:disable-next-line:no-parameter-reassignment
        cnt++;
        setTimeout(() => reloadIfFilesChanged(cnt, manifest), 1000);
    }
};