我使用Angularjs完成了一个完整的单页面应用程序(SPA)应用程序。
到目前为止一切顺利。
众所周知,所有javascript文件都是在第一次访问时加载的。或者,某些文件在需要时以惰性模式加载。
到目前为止一直很好......
情况是:服务器更新所有文件(html partials,javascripts,css's),客户端仍有大量文件过时。
这将简单解决刷新浏览器,点击F5键,控制+ f5或浏览器中的刷新按钮。但是在使用SPA时这个概念并不存在。
我不确定如何解决这个问题。
我可以以某种方式检测(可能执行ping操作)并只是重新加载该特定文件。使用document.write策略。但现在又出现了另一个问题,我有一个javascript文件,所有javascript都缩小了。
我可以尝试在浏览器中强制完全重新加载或强制重新登录(并重新加载,因为登录是SPA部分)。
但重新加载是一个丑陋的解决方案,想象客户端丢失表单中的所有数据,因为他不幸的是服务器刚刚更新。更糟糕的是,我现在必须创建一些“自动保存”功能。
我不确定如何处理这个问题,如果可能的话,以“有角度的方式”进行。
我想知道google gmail是如何处理这个问题的,因为我在没有记录的情况下保持了很长时间的记录。
答案 0 :(得分:1)
正如其他人已经建议的那样,将记录的用户保留在旧版本的webapp上。
不仅你在Angular中很难做到,但它也会导致糟糕的用户体验和令人惊讶的行为,因为用户使用旧版本和新版本之间可能没有映射。版本提供。可以删除,重命名,拆分或合并视图。相同视图的行为可能已经改变,并且在没有用户注意的情况下这样做可能会导致错误。
您使用Gmail做了一个示例,但可能已经注意到您在注销后总是会发生对用户界面的更改,而不是在您使用它时。
首先,如果您的应用是办公时间内使用的Intranet网站,只需在没有人使用时更新它。这是一个更简单的解决方案。
否则,如果您需要提供24/24的可用性,我的建议是:
部署新版本的SPA时,请将旧版本与新版本并行保留,将当前用户保留在旧版本上,并将新用户记录到新版本。这可以通过多种方式进行,具体取决于您的设置,但这并不难。
保留旧版本,直到您确信没有人仍在使用它,或者您确信新版本没问题并且您不需要回滚到旧版本。
后端服务应与旧版本的前端向后兼容。如果不可能,您也应该保留多个版本的后端服务。
答案 1 :(得分:1)
正如其他人所说,解决方案可以是对文件进行版本控制。因此,每次浏览器检查这些文件时,浏览器都会注意到文件与服务器中的文件不同,因此浏览器会缓存新文件。
我建议使用一些构建工具,如gulp,grunt或webpack,最后一个变得越来越流行。
到目前为止,我使用gulp进行项目。我正在转向webpack。
如果您对gulp感兴趣,可以查看gulp-rev和gulp-rev-replace插件。
他们做了什么?
假设我们在您的项目中有下一个文件app.js
将gulp-rev应用到您的项目之后得到的是类似app-4j8888dp.js
然后您的html文件,其中app.js是inject仍然指向app.js,所以你需要替换它。要做到这一点,你可以使用gulp-rev-replace插件。
例如。 gulp任务在哪里
var gulp = require('gulp');
var rev = require('gulp-rev');
var revReplace = require('gulp-rev-replace');
var useref = require('gulp-useref');
var filter = require('gulp-filter');
var uglify = require('gulp-uglify');
var csso = require('gulp-csso');
gulp.task("index", function() {
var jsFilter = filter("**/*.js", { restore: true });
var cssFilter = filter("**/*.css", { restore: true });
var indexHtmlFilter = filter(['**/*', '!**/index.html'], { restore: true });
return gulp.src("src/index.html")
.pipe(useref()) // Concatenate with gulp-useref
.pipe(jsFilter)
.pipe(uglify()) // Minify any javascript sources
.pipe(jsFilter.restore)
.pipe(cssFilter)
.pipe(csso()) // Minify any CSS sources
.pipe(cssFilter.restore)
.pipe(indexHtmlFilter)
.pipe(rev()) // Rename the concatenated files (but not index.html)
.pipe(indexHtmlFilter.restore)
.pipe(revReplace()) // Substitute in new filenames
.pipe(gulp.dest('public'));
});
如果您想了解更多详细信息,请参阅下面的链接。
https://github.com/sindresorhus/gulp-rev https://github.com/jamesknelson/gulp-rev-replace
答案 2 :(得分:0)
单页应用程序是一个单个堆栈,用于控制应用程序的客户端逻辑。因此,通过应用程序完成的任何导航都应该由您的客户端处理,而不是由服务器处理。目标是让一个“胖”HTTP请求加载您需要的所有内容,然后执行小的HTTP请求。
这就是为什么你的应用只能有一个ng-app
。您不应该拥有多个并且只需加载您需要的模块(尽管AngularJS团队希望以这种方式移动)。在所有情况下,您应该提供相同的缩小文件并处理应用程序中的所有内容。
在我看来,您更担心应用程序的状态。正如Tom Dale(EmberJS)在上一次Cage Match中所描述的那样,我们的目标应该是能够在任何时间点在服务器和客户端之间反映相同数据的应用程序。您可以通过cookie,会话或本地存储来实现此目的。
通常,SPA与基于REST的服务器通信,因此对您的数据执行idempotent操作。
tl; dr您不应该从服务器(例如样式或脚本)刷新任何内容,只需刷新应用程序正在处理的数据。最初的单一负载就是SPA的全部内容。
答案 3 :(得分:0)
分离您的数据和逻辑,并随时使用ajax重新加载数据,因为我建议您使用REST API从服务器获取数据。
SPA可帮助您一次又一次地减少HTTP请求,但它还需要一些http请求来更新要查看的新数据。
答案 4 :(得分:0)
好吧,您必须卸载旧的现有代码(即旧的AngularJS应用程序,模块,控制器,服务等)。从理论上讲,您可以创建一个自定义(随机)应用程序名称(所有模块都为每个唯一版本都有此前缀!),然后在浏览器中重建您的应用程序。但严重的是......这是a)非常复杂,b)可能因内存泄漏而失败。
所以,我的回答是:不要。
缓存问题
我个人建议为具有唯一ID的构建所依赖的所有资源命名/前缀;构建id,scm哈希,时间戳或类似的东西。因此,javascript的网址不是domain.tld/resources/scripts.js
而是domain.tld/resources-1234567890/scripts.js
,这可确保此路径的内容永远不会与(较新的)版本冲突。您可以根据需要选择路径/网址(取决于底层结构:它实际上是全部,您可以重新映射网址,等等)。甚至不要求每个版本永远存在(即将所有resources-(\d+)/
映射到resources/
;但是,这对于URL的概念来说并不好。
申请状态
嗯,问题是应用程序多久会发生变化,因此需要重新加载这一点很重要。 SPA在浏览器中打开多长时间?是否真的不能同时支持两个版本?客户端(浏览器中的应用程序)甚至可以在HTTP请求中发送自己的版本。
在新应用程序的开头,有很多需要重新加载的更改。但是,在您的应用程序处于固定状态后不久,更改将会更小,并且不需要重新加载。用户本身将进行更多刷新..比我们预期的更多:/
答案 5 :(得分:0)
正如其他人所说的那样......
不要,而且当socket.io可以正常工作时,如果你非常小心,那就要求麻烦了。
您有两个选项,在服务器更新时使之前的任何会话无效(在维护完成之前,我还会给用户半小时通知或5分钟,具体取决于应用程序。
第二个选项是版本控制。如果它们在版本10上,那么它们与后端10通信。如果您发布版本11并且它们仍然在10,那么它们仍然可以与后端10通信。
还记得Google wave吗?它失败的原因。太多人同时写一个来源会导致更多的问题然后解决。
答案 6 :(得分:0)
使用$ state服务。使用ctor在加载页面期间创建状态。在指定的时间后重新创建状态和加载页面。
function(state) {
state.stateCtor(action);
$state.transitionTo(action + '.detail', {}, {
notify: true
});
}
答案 7 :(得分:0)
对文件进行版本控制,以及每个更新增量版本号,浏览器将自动更新它。
答案 8 :(得分:0)
我的解决方案包含几点。
虽然这并不重要,但我将一个JavaScript文件发送到客户端,我使用grunt来准备我的版本。相同的grunt文件将带有版本号的查询添加到JavaScript标记中。无论您有一个文件还是大量文件,都需要为它们添加版本号。您可能还需要为资源执行此操作。 (最后查看示例)
我从服务器(我使用节点)发回所有应用程序的版本号回复。
在Angular中,当我收到任何响应时,我会根据加载的版本号检查版本号,如果它已更改(这意味着服务器代码已更新),那么我会提醒用户应用程序正在运行重装。我使用location.reload(true)
;
重新加载后,浏览器将再次获取所有新文件,因为脚本标记中的版本号现在不同,因此它不会从缓存中获取。
希望这有帮助。
<script src="/scripts/spa.min.js?v=0.1.1-1011"></script>