16

我已经使用 Angularjs 完成了一个完整的单页应用程序 (SPA) 应用程序。

到目前为止,一切都很好。

众所周知,所有 javascript 文件都是在第一次访问时加载的。或者,某些文件在需要时以惰性模式样式加载。

到目前为止,一切都很好...

情况是:服务器更新所有文件(html 部分、javascript、css),而客户端仍然有很多文件过时。

这可以简单地解决刷新浏览器,按 F5 键、control+f5 或浏览器中的刷新按钮。但是在使用 SPA 时,这个概念并不存在。

我不知道如何解决这个问题。

我可以以某种方式检测到(可能会执行 ping 操作)并且只是重新加载该特定文件。使用 document.write 策略。但现在出现了另一个问题,我有一个所有 javascript 都被缩小的 javascript 文件。

我可以尝试强制在浏览器中完全重新加载或强制重新登录(并重新加载,因为登录是 SPA 部分)。

但是重新加载是一个丑陋的解决方案,假设客户端丢失了表单中的所有数据,因为他很不幸服务器刚刚更新了。更糟糕的是,正因为如此,我现在必须创建一些“自动保存”功能。

如果可能的话,我不确定如何以“角度方式”处理这个问题。

我想知道 google gmail 是如何处理这个问题的,因为我在没有登录的情况下保持登录了很多小时。

4

9 回答 9

3

正如其他人已经建议的那样,将登录用户保留在旧版本的 webapp 上。

不仅你所要求的在 Angular 中很难做到,而且还可能导致糟糕的用户体验和令人惊讶的行为,因为用户对旧版本所做的操作与新版本提供的操作之间可能没有映射关系。可以删除、重命名、拆分或合并视图。同一视图的行为可能已经改变,并且在不通知用户的情况下这样做可能会导致错误。

您使用 Gmail 做了一个示例,但可能已经注意到 UI 的更改总是在您注销后发生,而不是在您使用它时发生。

首先,如果您的应用程序是办公时间使用的内网网站,则只需在无人使用时更新它。这是一个更简单的解决方案。

否则,如果您需要提供 24/24 可用性,我的建议是:

  • 当您部署新版本的 SPA 时,保持旧版本与新版本并行,将当前用户保留在旧版本上,并将新用户登录到新版本。这可以根据您的设置以多种方式进行,但这并不难。

  • 保留旧版本,直到您确信没有人仍在使用它,或者您非常确定新版本没问题并且您不需要回滚到旧版本。

  • 后端服务应该与旧版本的前端向后兼容。如果那不可能,您也应该保留多个版本的后端服务。

于 2015-06-03T17:00:10.757 回答
1

正如其他人所说的那样,解决方案可以是对文件进行版本控制。因此,每次您的浏览器检查这些文件时,浏览器都会注意到这些文件与服务器中的文件不同,因此浏览器会缓存新文件。

我建议使用一些构建工具,如 gulp、grunt 或 webpack,最后一个越来越流行。

到那时我将 gulp 用于我的项目。我正在转向 webpack。

如果你对 gulp 感兴趣,可以看看 gulp-rev 和 gulp-rev-replace 插件。

他们在做什么?

假设我们在您的项目中有下一个文件,您app.js在将 gulp-rev 应用于您的项目后得到的内容类似于app-4j8888dp.js您注入 app.js 的 html 文件仍指向 app.js,因此您需要替换它。为此,您可以使用 gulp-rev-replace 插件。

例如。吞咽任务在哪里

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

于 2017-03-11T10:38:07.013 回答
0

单页应用程序就是控制应用程序客户端逻辑的单个堆栈。因此,通过应用程序完成的任何导航都应该由您的客户端处理,而不是由服务器处理。目标是拥有一个单一的“胖”HTTP 请求来加载您需要的所有内容,然后执行小型 HTTP 请求。

这就是为什么您的应用程序中只能有一个ng-app。你不应该有多个并且只是加载你需要的模块(尽管 AngularJS 团队想要这样移动)。在所有情况下,您都应该提供相同的缩小文件并处理应用程序中的所有内容。

在我看来,您更担心应用程序的状态。正如上一次 Cage Match 中所描述的 Tom Dale (EmberJS),我们的目标应该是让应用程序能够在任何时间点在服务器和客户端之间反映相同的数据。您有很多方法可以做到这一点,无论是通过 cookie、会话还是本地存储。

通常,SPA 与基于 REST 的服务器通信,因此对您的数据执行幂等操作。

tl;dr 您不应该从服务器刷新任何东西(例如样式或脚本),只刷新应用程序正在处理的数据。初始单次加载是 SPA 的全部内容。

于 2013-09-12T20:04:45.447 回答
0

就像其他人所说的那样......

不要,虽然 socket.io 可以工作,但如果你非常小心,它会自找麻烦。

您有两个选择,在服务器更新时使任何先前的会话无效(我也会在维护完成之前给用户半小时或 5 分钟通知,具体取决于应用程序。

第二个选项是版本控制。如果它们在版本 10 上,那么它们与后端 10 通信。如果您发布版本 11,它们仍然在 10 上,那么它们仍然可以与后端 10 通信。

还记得 Google Wave 吗?它失败是有原因的。太多的人同时编写一个资源会导致更多的问题,而不是它解决的问题。

于 2014-07-03T17:02:01.970 回答
0

使用 $state 服务。使用 ctor 在加载页面期间创建状态。在指定时间后重新创建状态并加载页面。

function(state) {
  state.stateCtor(action);
  $state.transitionTo(action + '.detail', {}, {
    notify: true
  });
}

于 2014-09-28T09:22:41.663 回答
0

那么,您将不得不卸载旧的现有代码(即旧的 AngularJS 应用程序、模块、控制器、服务等)。从理论上讲,您可以创建一个自定义(随机)应用程序名称(所有模块都为每个唯一构建具有此前缀!),然后在浏览器中重新构建您的应用程序。但说真的......那是a)非常复杂,b)可能会由于内存泄漏而失败。

所以,我的回答是:不要。

缓存问题

我个人建议为具有唯一 ID 的构建所依赖的所有资源命名/添加前缀;构建 id、scm 哈希、时间戳或类似的东西。因此,javascript 的 url 不是domain.tld/resources/scripts.js,但domain.tld/resources-1234567890/scripts.js它确保此路径的内容永远不会与(较新的)版本冲突。你可以选择你想要的路径/url(取决于底层结构:它都是虚拟的,你可以重新映射 url,etcpp)。甚至不需要每个版本都将永远存在(即,将所有版本映射resources-(\d+)/resources/;但是,这对于 URL 的概念来说并不好。

应用状态

好吧,问题是应用程序多久会改变一次,这样重新加载是很重要的。SPA 在浏览器中打开多长时间?真的不可能同时支持两个版本吗?客户端(浏览器中的应用程序)甚至可以在 HTTP 请求中发送自己的版本。

在新应用程序的开始,有很多需要重新加载的更改。但是在您的应用程序具有固态后不久,更改将变得更小,并且不需要重新加载。用户本身将进行更多刷新.. 比我们预期的要多:/

于 2014-01-03T20:54:14.767 回答
0

分离您的数据和逻辑,并在需要时使用 ajax 重新加载数据,为此我建议您使用 REST API 从服务器获取数据。

SPA 可以帮助您一次又一次地减少 HTTP 请求,但它也需要一些 http 请求来更新要查看的新数据。

于 2013-12-24T12:28:44.023 回答
0

对文件进行版本控制,因此每次更新都会增加版本号,浏览器会自动更新它。

于 2015-08-26T23:56:13.733 回答
0

我的解决方案包括几点。

  1. 虽然这并不重要,但我将一个 JavaScript 文件发送到客户端,我使用 grunt 来准备我的发布。同一个 grunt 文件将带有版本号的查询添加到 JavaScript 标记。无论您有一个文件还是很多文件,您都需要为它们添加一个版本号。您可能还需要对资源执行此操作。(查看最后的示例)

  2. 我从服务器(我使用节点)发回我的所有响应应用程序的版本号。

  3. 在 Angular 中,当我收到任何响应时,我会根据加载的版本号检查版本号,如果它已更改(这意味着服务器代码已更新),那么我会提醒用户应用程序将重新加载。我用location.reload(true);

  4. 一旦重新加载,浏览器将再次获取所有新文件,因为脚本标签中的版本号现在不同了,因此它不会从缓存中获取它。

希望这可以帮助。

<script src="/scripts/spa.min.js?v=0.1.1-1011"></script>
于 2017-02-24T16:33:38.850 回答