我正在使用MEAN堆栈(mongo,express,angular和node)。我每隔几天就会相对频繁地部署到生产中。我担心的是我有时会更改客户端代码和API,我宁愿不必确保API与以前版本的客户端代码的向后兼容性。
在这种情况下,当我推向生产时,确保所有客户重新加载的最有效方法是什么?我已经看过,例如Evernote有一个弹出窗口,上面写着一些内容,请重新加载浏览器以获取最新版本的Evernote。我想做类似的事情...我需要沿着socket.io或sock.js的路径走下去,还是我错过了一些简单的东西,有一种更简单的方法来实现这个目标?
答案 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 tag
和js
文件中定义您的版本,如果不匹配,则显示弹出窗口并告诉用户刷新。
答案 2 :(得分:1)
我会先告诉你我的问题,然后我会推荐一个暂定的解决方案。我想强制我的用户注销,然后在部署生产版本时登录。在任何时候,生产中都会部署两个版本的软件。 FE 知道的软件版本和 Backend 知道的版本。大多数情况下,它们是相同的。在任何时候,如果它们不同步,那么我们需要重新加载客户端,让客户端知道已经推送了新的生产版本。
我假设后端有 99.99% 的时间了解生产中已部署软件的最新版本。
以下是我想推荐的两种方法:-
后端 API 应始终在响应标头中返回软件的最新版本。在前端,我们应该有一段通用的代码来检查 API 返回的版本和 FE 上显示的版本是否相同。如果没有,则重新加载。
每当用户登录时,BE 应在 JWT 中对最新的软件版本进行编码。并且 FE 应该继续将其作为不记名令牌与每个 API 请求一起发送。 BE 还应该为每个 API 请求编写一个通用拦截器。这将比较从 API 请求收到的 JWT 中的软件版本和
答案 3 :(得分:0)
答案 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);
}
};