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

How to force client reload after deployment?

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

在这种情况下,确保在我推送到生产环境时所有客户端重新加载的最有效方法是什么?例如,我看到 Evernote 有一个弹出窗口,上面写着“请为最新版本的 Evernote 重新加载浏览器”。我想做一些类似的事情...我是否需要沿着 socket.io 或 sock.js 的路径前进,或者我是否遗漏了一些简单的东西并且有更简单的方法来实现这一点?

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

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

我知道 rails 有资产管道来处理它,但我不熟悉 MEAN stacknpm 中应该有一些用于该目的的包。

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

更新: AppCache 已于 2015 年夏季弃用,因此以下不再是最佳解决方案。新的建议是改用 Service Workers。然而,Service Workers 目前仍在 IE 和 Safari 中进行粗略(阅读:可能没有)支持的实验。

或者,许多构建工具现在无缝结合 cache-busting 和文件 "versioning" 技术来解决 OP 问题。 WebPack 可以说是 space 中的现任领导者。


这可能是使用 HTML5 的 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。

其次,在任何要使用应用程序缓存的页面上,将 header 标记修改为:

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

最后,您需要添加一些 Javascript 来检查应用程序缓存是否有任何更改,如果有,请对其进行处理。这是 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 端点,returns 当前构建 ID 或上次部署 date-time.您的 Angular 应用程序偶尔会访问此端点并将结果与​​其内部版本进行比较,如果不同,则重新加载自身。

或者,您可以考虑使用 live-reload 脚本 (example),但是,虽然对开发很有帮助,但我不确定使用 live/in-place-reloading 生产中的资产。

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

/foo/bar/main.js?123

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

这也意味着我可以让缓存过期 header,无论我想要多长时间。

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

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

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);
    }
};

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

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

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

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

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

    收到的 JWT 中的软件版本