【问题标题】:Caching URL view/state with parameters使用参数缓存 URL 视图/状态
【发布时间】:2014-12-22 20:30:16
【问题描述】:

我正在使用 Cordova 和 AngularJS 制作一个移动应用程序。目前我已经安装了用于路由的 ui-router,但我对任何其他路由选择持开放态度。

我的愿望:我想缓存某些与参数绑定的视图。换句话说,我想缓存路径(或页面)。

示例情况:假设我们看到某个仪表板页面,单击某个书籍封面,该封面重定向到路径book/2。此路径首次加载到应用程序中。路由器从HomeController 重定向到BooksController(无论名称如何)。现在BooksController 为给定的$stateParams (book id = 2) 加载数据并创建包含所选书籍信息的视图。

在这种情况下我想要什么:

  1. 我返回仪表板页面 - 它已经加载(缓存?)
  2. 我再次选择第 2 本书
  3. 控制器或路由器注意到关于这本书的数据已经加载
  4. 视图并未重新创建,而是从缓存中获取

其实,最好根据路径缓存我访问的所有内容。预加载也很酷。

原因:性能。当我打开一些书籍列表时,我希望它快速显示。每次都创建视图时,页面更改的动画看起来很糟糕(不流畅)。

任何帮助将不胜感激。

编辑:

首先,由于我认为这是许多移动 HTML 应用程序程序员的常见问题,因此我想提供一些信息:

  1. 我不是在寻找黑客,而是在寻找一个明确的解决方案如果可能的话。
  2. 视图中的数据使用 AngularJS,所以是的,有 诸如 ng-bindng-repeat 之类的东西。
  3. 数据DOM 元素 都需要缓存。据我所知,浏览器的布局操作并不像重新创建整个 DOM 树那样昂贵。而重绘也不是我们可以省略的。
  4. 拥有单独的控制器是很自然的事情。因为我可以不带它离开,所以我无法想象它会如何工作。

我有一些半解决方案,但我会严格要求自己的愿望。

解决方案 1。

将所有视图放入一个文件(我可以使用 gulp builder 来完成)并使用ng-show。这是最简单的解决方案,我相信任何了解 AngularJS 的人都不会考虑它。

一个绝妙的技巧(来自@DmitriZaitsev)是创建一个辅助函数来根据当前位置路径显示/隐藏元素。

优点:

  1. 很简单。
  2. 某种预加载功能。

缺点:

  1. 所有视图都必须在一个文件中。不要问为什么不方便。
  2. 既然都是关于移动设备的,有时我想“清除”内存。我能想到的唯一方法是从 DOM 中删除这些孩子。脏但还可以。
  3. 我无法轻松地同时缓存 /book/2 和 /book/3。我必须在一些模板之上为每个与参数绑定的视图动态创建 DOM 子节点。

解决方案 2。

使用来自ui-router-extras 的粘性状态和未来状态,这太棒了。

优点:

  1. 分离视图。
  2. 用法很清楚,很简单,因为它只是ui-router的插件。
  3. 可以创建动态子状态。所以可以缓存book1book2,但我不确定book/1book/2

缺点:

  1. 同样,我不确定,但我没有找到缓存 pair/tuple (view, parameters) 的示例。除此之外,它看起来很酷。

【问题讨论】:

  • 答案很大程度上取决于您尝试缓存的视图中的内容。
  • 所有绑定的东西,比如ng-repeat等等。所以我想这不仅仅是使用$templateCache
  • 我会尝试使用 ngHide 而不是实际替换视图。当然,这意味着重写你的一些代码。也许将两个 - nghide / ngshow结合在仪表板和书之间,但是在选择新书时,书籍视图更改。 (我想包含另外两个的第三个作用域/控制器对于保留路由是必要的。)
  • 嗯,这是非常简单的方法,我希望有一个比隐藏 div 更通用的解决方案。示例中描述的情况并不真实,因为我不想污染我想要的感觉。实际上,我来自 gamedev,我很失望缓存结果在 webdev 中并不常见(或者我的看法不同,不确定)。
  • 您尝试过ui-router-extras 中的粘性状态吗?还是这还不够?

标签: javascript angularjs angular-ui-router angular-routing


【解决方案1】:

如果您使用的是 ui.router,那么您应该看看 ui.router extras,特别是粘性状态模块。这允许您缓存状态“树”(包括渲染视图),因此不必在状态更改时编译或重新渲染它们。

http://christopherthielen.github.io/ui-router-extras/

这是一个演示:

http://christopherthielen.github.io/ui-router-extras/example/sticky/#/

【讨论】:

  • 谢谢,但该扩展已在 cmets 中提及并在我更新的帖子中链接。
  • 见上面的评论。 UI-router 在动态添加状态/视图方面不是很好,当然也不支持在您想要关闭视图时删除它们。如果您想在静态定义的状态之间保留视图,粘性状态会很好用。我遇到了不断的竞争条件,试图动态添加状态并导航到它们(在我的选项卡中使用了 Angular Material)。
【解决方案2】:

这正是我必须为我的网站33hotels.com 解决的问题。您可以检查并使用选项卡“过滤器”和“过滤器列表”(对应不同的路由),并看到视图立即更新,没有任何延迟!

我是怎么做到的?这个想法非常简单 - 摆脱路由器!

为什么?因为路由器的工作方式是重新编译视图在每一个单独的路由更改。是的,Angular 会缓存 Template,但不会缓存填充数据的已编译视图。即使数据不改变!结果,当我过去使用路由器时,开关总是感觉迟钝且没有反应。每次我能注意到恼人的延迟,它只是几分之一秒,但仍然很明显。

现在我使用的解决方案是什么?不要重新编译您的视图!始终将它们保存在您的 DOM 中!然后根据路线使用ng-hide/ng-show 隐藏/显示它们:

<div ng-show="routeIs('/dashboard')">
    <-- Your template for Dashboard -->
</div>

<div ng-show="routeIs('/book')">
    <-- Your template for Book -->
</div>

然后在你的Controller 中创建一个函数routeIs(string) 来测试$location.path() 是否匹配string,或者在我使用它时以string 开头。这样我仍然可以查看所有路径,例如/book/2。这是我正在使用的功能:

$scope.routeBegins = function () {
    return _.some(arguments, function (string) {
           return 0 === $location.path().indexOf(string);               
    });
};            

所以不需要聪明地缓存 - 只需将它保存在 DOM 中。它会为您缓存您的视图!

最好的部分是 - 每当您的数据发生更改时,Angular 都会立即更新您 DOM 中的所有视图,甚至是隐藏的视图!

为什么这么棒?因为,作为用户,我不必在我想看到结果的那一刻等待所有的解析和编译。我想单击选项卡并立即查看我的结果!为什么站点要等我点击它然后然后在我等待的时候开始所有重新编译?尤其是在我的计算机空闲期间,这可以很容易地完成。

有什么缺点吗?我能想到的唯一真正的一个是用更多的 DOM 元素加载内存。但是,我的视图的实际字节大小可以忽略不计,例如比较包含所有 JS、CSS 和图像。

另一个可能但可以避免的缺点是隐藏视图的重新编译成本。这是您可以根据当前路线变得聪明并避免计算繁重的部分的地方。 此外,您无需重新编译整个 View,仅重新编译受数据更改影响的部分,这也降低了计算成本。

我发现每个人都在使用 Routes 并且似乎完全不知道(或不知道)这个问题是非常了不起的。

【讨论】:

  • 感谢您的努力。这种方法不太灵活,这就是我要求替代方案的原因。我相信没有人会在这里提出这个半解决方案,但现在我知道我错了,我没有具体说明。不重新编译视图是我的愿望而不是解决方案。 ng-show 方法的缺点: 1. 所有视图都必须在一个文件中。不要问为什么这么烂。 2. 有时我想“清除”记忆。我能想到的唯一方法是从 DOM 中删除这些孩子。 3. 我不能同时缓存 /book/2 和 /book/3。我必须在一些模板之上创建动态 DOM 子级。
  • 但无论如何,我认为您确实付出了更多努力来理解我的问题,所以再次感谢。无论如何,我不会将此标记为答案,因为也许有更好的解决方案。
  • @Namek 1. 视图不需要需要在单个文件中!使用ng-include!
  • @Namek 2. 要从视图中清除内存 - 请改用 ng-if。但仅对于大量视图,您才能提供差异。但是,对于良好的架构,情况绝不应该如此!绝对没有理由向您的用户提供如此庞大的视图,因为节省内存是有意义的。在我看来。要么页面太长,要么细节太多。在第一种情况下,您应该动态隐藏不可见的部分,在第二种情况下,您会压倒用户。在任何情况下,它都必须重新编译视图,因此您可能会失去优势。只有测试才会显示。
  • @Namek 3. 我不明白这个问题。您可以轻松地缓存已编译的 HTML 并将其存储在您想要的任何位置。如果你想缓存模板,Angular 无论如何都会这样做,但你必须以这种或其他方式用新数据编译它。关键是在设备空闲时尽早完成。
【解决方案3】:

1) 关于应用程序中的静态页面(视图),Angular 负责加载它们。

例如:对于您的仪表板页面,您不必担心缓存页面,因为 Angular 会处理它。 Angular 只会在仪表板视图的所有下一个请求中加载仪表板视图一次,如果它是一个静态视图,而没有通过 ajax 调用加载任何数据,则 Angular 只会显示视图(不加载视图文件)。

2) 如果您的仪表板本身通过 ajax 加载图书列表(或类似数据),那么您可以告诉您的控制器只加载一次数据并将其存储到 localstorage 并且对仪表板页面的后续请求只能加载来自 localStorage 的数据。

3) 当您的BooksController 将数据加载到视图中时,可以使用类似的方法。您可以检查您的 BooksController 是否先前已提出对特定书籍的请求,如果没有,您可以将其存储到本地存储或数据库。如果之前请求了数据,那么您可以从存储中加载相同的数据,而无需向服务器发出请求。

示例情况:

假设用户请求 book1,然后

  1. 您的控制器,即 BooksController 检查之前是否请求过相同的数据,
  2. 如果没有,您可以通过 ajax 调用从服务器加载数据并将其保存到本地存储。
  3. 如果它在加载之前加载,您将加载存储在本地存储或数据库中的数据。

【讨论】:

  • 感谢您的意见。您注意到的事情相当简单和常见。对不起,但我觉得你完全误解了这个问题。也没有任何 ajax 调用和静态页面。它更多的是关于摘要性能(基于数据创建 DOM 树),而不仅仅是记住数据。我想要的是为同一视图的许多 URL 缓存整个 DOM。例如,我的路线category/{id} 包含一些书籍列表,我想缓存所有这些“单独的”视图:category/1category/2category/3。所以这都是关于路由的。
  • (由于字符限制而继续)当我打开category/4 时,它会从缓存中获取页面或根据数据加载它。但是当我再次打开category/4 时,它肯定会从缓存中获取它。这就是我想要的。也许有一些没有人使用的内置机制,或者有一个类似用途的库。
  • 您应该稍微编辑一下问题,以便其他人可以帮助您。
猜你喜欢
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
  • 2022-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-15
  • 1970-01-01
相关资源
最近更新 更多