AngularJS使用$ rootScope作为数据存储

AngularJS using $rootScope as a data store

我对我的AngularJS应用程序有一个想法,我很好奇AngularJS社区是否会认为这样做可以……

简而言之,我正在连接到一个数据API并在页面上显示我的结果。我已经创建了一个Angular服务,它在$rootscope.data store上创建了一个数据存储。我还有一个服务方法,它用从API端点返回的数据更新数据存储。如果我使用datastore.update("products")从控制器内部请求"products"API端点,这将使用我的产品数据更新$rootscope.datastore.products。现在,在视图/部分视图中,我只需要说ng repeat="product in datastore.products"来显示我的数据,不管我在哪个控制器范围内。所以,本质上,我的数据存储是我唯一的真理来源。

我觉得我从这个方法中得到的是易于理解的语义和最少的控制器编码。因此,只要更新数据存储,绑定到数据存储的任何内容都会得到更新。

这会给$rootscope摘要循环带来太多的负载吗,还是这只是一种奇怪的方式?或者这是一个非常棒的方式?:)欢迎发表任何评论。

  • 您好,您可以发布创建数据存储或提供一些指针的服务的代码吗?我也有类似的想法。谷歌搜索让我找到了你的帖子。


此问题在此处引用的AngularJS常见问题解答中解决:

Occasionally there are pieces of data that you want to make global to
the whole app. For these, you can inject $rootScope and set values on
it like any other scope. Since the scopes inherit from the root scope,
these values will be available to the expressions attached to
directives like ng-show just like values on your local $scope.

团队似乎确实鼓励这样使用$rootScope,但要注意:

Of course, global state sucks and you should use $rootScope sparingly,
like you would (hopefully) use with global variables in any language.
In particular, don't use it for code, only data. If you're tempted to
put a function on $rootScope, it's almost always better to put it in a
service that can be injected where it's needed, and more easily
tested.

Conversely, don't create a service whose only purpose in life is to
store and return bits of data.

这不会给$digest循环带来太多的负载(它执行基本的脏检查来测试数据突变),这不是一种奇怪的方法。

编辑:有关性能的更多详细信息,请参阅misko(angularJS dev)在这里给出的答案:在angularJS中数据绑定是如何工作的?特别注意性能部分。

  • 因为他已经有了这个服务,而且它的一部分工作也是管理API(不仅仅是存储和返回),所以在这种情况下,这似乎是反对使用$rootscope的。
  • 就我个人而言,我更喜欢将$rootScope的数据保留在外,但这里是常见问题的认可。因此,由于使用$rootScope的方式并没有真正的问题,所以这取决于程序员的偏好,以及在任何给定的应用程序中什么是最有意义的。
  • 感谢大家的意见!
  • 感谢指出了使用$rootscope存储(小心)数据而不是服务更好的部分。
  • This does not put too much load on the $digest cycle (which implements basic dirty checking to test for data mutation) and this is not an odd way to do things.什么?-有人能翻译吗?
  • @本本吉爵士,你需要翻译成哪种语言;—)
  • "基本脏检查"和"数据突变"并不是我认为的标准术语。你能详细解释一下吗?
  • 这一点也让我困惑。我不明白如何声明摘要循环实现了基本的脏检查,这意味着它不会对摘要循环施加太多的负载。对我来说,这句话只是承认这样一个事实,$rootscope上的所有变量都是在摘要循环中检查的,更多的变量=较慢的循环。
  • 插入语部分并不意味着什么;它是附加信息。请阅读以下两个链接上的性能部分:blog.bguiz.com/post/57373805814/…--stackoverflow.com/questions/9682092/…。
  • 至于$rootScope$digest周期的影响,我认为这个问题还不太清楚。$digest脏检查所有被监视的表达式,而不是整个范围。但是,如果您的$rootScope中有太多的变量可用作参数来评估这些表达式,则会产生影响。
  • @我建议你读一下米斯科的帖子。您可能需要数千或10000块手表才能开始体验性能问题。javascript很快。


为了安抚所有人,为什么不直接使用$cachefactory呢?这允许数据请求服务是无状态的,基本上只是一个getter和setter。我承认将数据保存在$rootscope上或作为服务中的一个属性是方便的,但感觉是错误的。使用$cachefactory也很容易。

首先创建缓存服务:

1
2
3
4
angular.module('CacheService', ['ng'])
    .factory('CacheService', function($cacheFactory) {
    return $cacheFactory('CacheService');
});

在app.js中包含js文件,然后将其插入到app声明中:

1
var MyApp = angular.module('MyApp', ['CacheService']);

注入到服务中,这样使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'use strict'

MyApp.factory('HackerNewsService', function(CacheService) {
    return {
        getNews: function(key) {
            var news = CacheService.get(key);

            if(news) {
                return news;
            }

            return null;
        },
        setNews: function(key, value) {
            CacheService.put(key, value);
        },
        clearNews: function(key) {
            CacheService.put(key, '');
        }
    };
});

现在,您所要做的就是将您的hacker新闻服务注入到控制器中,并通过调用我们在其上创建的方法来使用它。例如:

1
2
HackerNewsService.setNews('myArticle', {headline: 'My Article', body: 'This is the body'});
$scope.article = HackerNewsService.getNews('myArticle');

  • 嗨,布雷克,你的评论太棒了!并向所有新手解释了细节。这是服务!我正在我的AngularJS自助教程应用程序上尝试,以便在根作用域中使用这样的缓存服务,而不是我的数组。
  • 伟大的!解释得很好!
  • 我刚开始使用AngularJS,想知道,当用户关闭或重新打开应用程序或重新启动手机时,这个cahche会保留吗?
  • 你必须把它绑在本地的仓库里或者类似的地方。如果您刚开始使用角度,则应停止并使用react;)


我的经验是,使用$rootscope来存储我的数据模型中我的应用程序中所有NGVIEW所共有的部分,这是最方便的方法。

1
{{mymodel.property}}

对我来说比

1
{{getPropertyModel()}}

使用javascript

1
2
3
4
5
6
7
8
9
10
11
12
app.factory('MyModel', function(){
    return {
        getModel: function() { ... },
        setModel: function(m) { ... },
    }
});

app.controller('ctrl', ['$scope', 'MyModel', function($scope, MyModel){
    $scope.getPropertModel = function() {
        return MyModel.getModel().property;
    };
}]);

如果使用服务或缓存工厂,则HTML模板中对模型的每个访问都将成为一个函数,这比访问rootscope的属性更不可读。使用$rootscope可以减少代码,从而减少错误和测试。

当然,只有所有ngview的公共部分存储在$rootscope中。模型的其余部分存储在本地$scope中。

函数上的监视也比对象属性上的监视慢。所以从性能上来说,$rootscope也更好。


我正面临着同样的问题,在我看来,将它存储在全球可用的"位置"是正确的方法,但是$rootscope不是理想的地方。

我刚刚做了更多的研究,您可以考虑使用"服务"来管理您的数据/单独的关注点,而不是将您的数据存储在$rootscope上(尤其是最后一个代码示例):http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/

然后,在使用该方法创建的"服务"中,无论您将数据保存在内存、缓存工厂、本地存储(如本文所述)和/或数据库(如通过Ajax),都可以满足应用程序的任何需要。它还意味着可以根据需要独立地更改存储数据的方式。


我想我不知道你为什么要用根镜?服务实例的生命周期也是整个应用程序的生命周期,因此无论您使用什么数据模式/语义,都可以直接存储在服务本身中,并且可以在控制器之间共享。然而,这些方法中的任何一种都不能像使用本地存储那样在刷新中存活下来。

剩下的部分听起来像是一种懒惰的加载方法。如果服务是唯一"知道"数据是否已从远程加载的东西,并且如果数据已被缓存并缓存,则返回它,如果数据未被缓存并缓存,则返回它?如果我正确理解那部分,它就是一个好模式。

编辑:在这里,我采用了类似于懒惰加载的方法,注意缓存只是在服务本身中:

1
2
3
4
5
6
7
8
9
10
angular.module('HN_Reddit_Mashup.Services', [])
    .factory('HackerNews', function($http) {
        var HackerNewsCache = {};
        return {
            get: function(url) {
                return HackerNewsCache[url] ||
                    (HackerNewsCache[url] = $http.jsonp("http://api.thriftdb.com/api.hnsearch.com/items/_search?q=" + url +    "&callback=JSON_CALLBACK"));
            },                
        };
    })

  • 我将数据存储放在$rootscope上的主要原因是我的视图/部分和语义上的访问非常简单。datastore.endpoint.some_data对我来说似乎很重要。你知道数据来自哪里,不需要做很多控制器的工作。只需告诉服务使用类似data store.update("endpoint");)的内容更新数据存储。谢谢你的评论!
  • 从这个词的角度来看,"服务"是多余的,它实际上只是一个效用函数。管理rootscope访问。
  • 我喜欢将东西保存在服务中,并在单个范围内谨慎地公开它,这样我就不会变得"疯狂",开始严重依赖于UI中长期存在的"东西"的对象模型。如果每个范围只有与手头的任务相关的内容,那么如果你需要或想要重构"东西",你就不太可能遇到麻烦(或者更糟,工作)。这是一个风格上的选择,对我来说似乎是有益的。此外,当消化$rootscope时,分配给它的任何内容都将被脏检查——这(理论上)可能会对性能产生可测量的影响。
  • 在我看来,你应该避免使用rootscope。使用根镜将使测试非常困难。相反,我认为您应该考虑(如其他人提到的)将数据放入服务本身。这也将帮助您更容易地维护代码,因为您将所有代码放在一个地方(服务)。对我来说,听起来你太懒了,把服务注入控制器,哈哈…(只是开玩笑)