AngularJS:过时的 UI 尝试加载缓存无效的 HTML 文件,这些文件在部署新版本时不再存在
AngularJS: stale UI trying to load cachebusted HTML files that no longer exist when new version deployed
我有一个 angularjs/ui-router 应用程序。所有由框架延迟加载的 html 模板都被缓存清除,因此它们被命名为 myTemplate.someHash.html
,并且对这些模板的引用也被更新 compile-time.
有时会出现一个问题,当我在用户使用应用程序时部署新版本。事件顺序如下:
- 用户打开了一个页面,该页面上有一个 link 到一个名为 Summary 的状态。 Summary 状态使用名为
summary.html
的 HTML 模板。但是由于我们正在对 HTML 模板进行缓存清除,因此该文件实际上在用户浏览器中当前加载的应用程序版本中被命名为 summary.12345.html
。
- 部署了一个新版本,其中包含对
summary.html
的一些更改,导致它获得新的哈希值,因此现在称为 summary.98765.html
。
- 用户点击 link,angular 尝试获取不再存在的
summary.12345.html
,导致 404。
有解决这个问题的好模式吗?
我最初的想法是在所有请求中附加一些 HTTP header,例如 Expected-Version: 999
(其中 999 是在 CI 中生成的内部版本号),如果不是服务器上的版本 运行,然后服务器将响应类似“410 Gone”的内容,在这种情况下,应用程序将要求用户刷新浏览器。
但这需要在服务器端做一些工作,而且我也不确定如何将此逻辑注入到客户端的模板加载中。
此外,由于新版本通常每周部署几次(有时每天多次),而且大多数这些版本甚至不包含任何会以上述方式破坏 SPA 的更改,所以我不不想强迫用户一直重新加载页面。
当请求导致 404 AND 响应包含一个 header 表明您' 运行 SPA 的旧版本?
或者有更简单的方法?
我是按照下面的方法解决的
我决定不想在每次部署新版本时都要求用户刷新浏览器,因为其中许多版本仅包含后端更改。
我只想要求他们在部署发布后刷新(从他们在浏览器中打开应用程序开始),其中包含以下任一更改:
- javascript 应用程序(作为两个捆绑包提供),或
- angular 延迟加载的任何 html 模板。
所以我不得不引入 UI 版本的概念。我决定这个 UI 版本的定义应该是上面提到的 html 模板的所有文件名的 SHA256 散列(只是名称,因为那些已经包含用于缓存清除的散列),与名称连接两个 js 包中的一个(它们也是缓存无效的)。
这是解决方案。
第 1 部分
我在我的 CI 管道中添加了一个步骤,在 js 应用程序编译和缓存清除之后,但在整个应用程序构建到 Docker 图像之前。这个新的构建步骤如下所示:
const glob = require("glob");
const crypto = require('crypto');
const fs = require('fs');
const jsPromise = new Promise(resolve => {
glob("./js/*.js", {}, function (err, files) {
if (!err) {
resolve(files);
}
});
});
const htmlPromise = new Promise(resolve => {
glob("./app/**/*.html", {}, function (err, files) {
if (!err) {
resolve(files);
}
});
});
Promise.all([jsPromise, htmlPromise]).then(([files1, files2]) => {
const allFiles = files1.concat(files2);
fs.writeFileSync(
'./ui-version.env',
`UI_VERSION=${crypto.createHash('sha256').update(allFiles.sort().join()).digest('hex')}`
);
});
如您所见,它将其写入名为 ui-version.env
的文件中。然后,我使用 env_file
命令在 docker-compose.yml 中引用此文件。这样,后端应用程序现在知道当前的 UI 版本。后端现在在 HTTP 端点 GET /ui-version
.
提供此数据
第 2 部分
加载前端应用程序时,它会调用上述端点并将当前 UI 版本存储在内存中。
部署新版本后,会向所有连接的前端应用程序发送一条 websocket 消息,并发出通知以检查它们是否仍 运行 是最新的 UI版本。他们通过请求上面提到的新端点并将结果与它在加载时保存的版本进行比较来做到这一点。
发送 websocket 消息时计算机处于睡眠模式或没有互联网连接的用户怎么办?
我已经在 SPA 中设置了事件侦听器,当计算机从睡眠模式恢复或恢复互联网连接时,该事件侦听器会关闭。因此,任何这些事件现在也会触发 UI 版本检查。
老实说,我对这个解决方案不是很感兴趣。有很多活动部件。如果有人可以提供更简单的解决方案,那就太好了。但不幸的是,我怀疑我是否会得到任何好的答案。
我有一个 angularjs/ui-router 应用程序。所有由框架延迟加载的 html 模板都被缓存清除,因此它们被命名为 myTemplate.someHash.html
,并且对这些模板的引用也被更新 compile-time.
有时会出现一个问题,当我在用户使用应用程序时部署新版本。事件顺序如下:
- 用户打开了一个页面,该页面上有一个 link 到一个名为 Summary 的状态。 Summary 状态使用名为
summary.html
的 HTML 模板。但是由于我们正在对 HTML 模板进行缓存清除,因此该文件实际上在用户浏览器中当前加载的应用程序版本中被命名为summary.12345.html
。 - 部署了一个新版本,其中包含对
summary.html
的一些更改,导致它获得新的哈希值,因此现在称为summary.98765.html
。 - 用户点击 link,angular 尝试获取不再存在的
summary.12345.html
,导致 404。
有解决这个问题的好模式吗?
我最初的想法是在所有请求中附加一些 HTTP header,例如 Expected-Version: 999
(其中 999 是在 CI 中生成的内部版本号),如果不是服务器上的版本 运行,然后服务器将响应类似“410 Gone”的内容,在这种情况下,应用程序将要求用户刷新浏览器。
但这需要在服务器端做一些工作,而且我也不确定如何将此逻辑注入到客户端的模板加载中。
此外,由于新版本通常每周部署几次(有时每天多次),而且大多数这些版本甚至不包含任何会以上述方式破坏 SPA 的更改,所以我不不想强迫用户一直重新加载页面。
当请求导致 404 AND 响应包含一个 header 表明您' 运行 SPA 的旧版本?
或者有更简单的方法?
我是按照下面的方法解决的
我决定不想在每次部署新版本时都要求用户刷新浏览器,因为其中许多版本仅包含后端更改。
我只想要求他们在部署发布后刷新(从他们在浏览器中打开应用程序开始),其中包含以下任一更改:
- javascript 应用程序(作为两个捆绑包提供),或
- angular 延迟加载的任何 html 模板。
所以我不得不引入 UI 版本的概念。我决定这个 UI 版本的定义应该是上面提到的 html 模板的所有文件名的 SHA256 散列(只是名称,因为那些已经包含用于缓存清除的散列),与名称连接两个 js 包中的一个(它们也是缓存无效的)。
这是解决方案。
第 1 部分
我在我的 CI 管道中添加了一个步骤,在 js 应用程序编译和缓存清除之后,但在整个应用程序构建到 Docker 图像之前。这个新的构建步骤如下所示:
const glob = require("glob");
const crypto = require('crypto');
const fs = require('fs');
const jsPromise = new Promise(resolve => {
glob("./js/*.js", {}, function (err, files) {
if (!err) {
resolve(files);
}
});
});
const htmlPromise = new Promise(resolve => {
glob("./app/**/*.html", {}, function (err, files) {
if (!err) {
resolve(files);
}
});
});
Promise.all([jsPromise, htmlPromise]).then(([files1, files2]) => {
const allFiles = files1.concat(files2);
fs.writeFileSync(
'./ui-version.env',
`UI_VERSION=${crypto.createHash('sha256').update(allFiles.sort().join()).digest('hex')}`
);
});
如您所见,它将其写入名为 ui-version.env
的文件中。然后,我使用 env_file
命令在 docker-compose.yml 中引用此文件。这样,后端应用程序现在知道当前的 UI 版本。后端现在在 HTTP 端点 GET /ui-version
.
第 2 部分
加载前端应用程序时,它会调用上述端点并将当前 UI 版本存储在内存中。
部署新版本后,会向所有连接的前端应用程序发送一条 websocket 消息,并发出通知以检查它们是否仍 运行 是最新的 UI版本。他们通过请求上面提到的新端点并将结果与它在加载时保存的版本进行比较来做到这一点。
发送 websocket 消息时计算机处于睡眠模式或没有互联网连接的用户怎么办?
我已经在 SPA 中设置了事件侦听器,当计算机从睡眠模式恢复或恢复互联网连接时,该事件侦听器会关闭。因此,任何这些事件现在也会触发 UI 版本检查。
老实说,我对这个解决方案不是很感兴趣。有很多活动部件。如果有人可以提供更简单的解决方案,那就太好了。但不幸的是,我怀疑我是否会得到任何好的答案。