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中数据绑定是如何工作的?特别注意性能部分。


为了安抚所有人,为什么不直接使用$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');


我的经验是,使用$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"));
            },                
        };
    })