【问题标题】:AngularJS using $rootScope as a data storeAngularJS 使用 $rootScope 作为数据存储
【发布时间】:2013-05-20 07:08:56
【问题描述】:

我对我的 AngularJS 应用程序有一个想法,我很好奇 AngularJS 社区是否会认为这样做可以。简而言之,我正在连接到数据 API 并在页面上显示我的结果。

我创建了一个 AngularJS 服务,它在 $rootScope.DataStore 上创建一个数据存储。我还有一个服务方法,它使用从 API 端点返回的数据更新 DataStore。如果我使用 DataStore.update('products') 从控制器内部请求“产品”API 端点,这将使用我的产品数据更新 $rootScope.DataStore.products。

现在,在视图/部分中,我需要做的就是说 ng-repeat="product in DataStore.products" 来显示我的数据,而我所在的控制器范围无关紧要。所以,从本质上讲,我的 DataStore 是我的单一来源真相。

我觉得从这种方法中获得的好处是易于遵循语义和最少的控制器编码。因此,只要 DataStore 更新,任何绑定到 DataStore 的内容也会更新。

这是否会给$rootScope 摘要循环带来过多的负担,或者这只是一种奇怪的方法?或者这是一个非常棒的方式? :) 欢迎任何 cmets。

【问题讨论】:

    标签: javascript html angularjs angularjs-scope


    【解决方案1】:

    这里引用的AngularJS FAQ 解决了这个问题:

    有时您希望将某些数据设为全局 整个应用程序。对于这些,您可以注入 $rootScope 并设置值 它像任何其他范围一样。由于范围从根范围继承, 这些值将可用于附加到的表达式 像 ng-show 这样的指令就像你本地 $scope 上的值一样。

    团队似乎确实鼓励以这种方式使用$rootScope,但需要注意的是:

    当然,全局状态很糟糕,您应该谨慎使用 $rootScope, 就像您(希望)在任何语言中使用全局变量一样。 特别是,不要将其用于代码,仅用于数据。如果你想 在 $rootScope 上放一个函数,最好把它放在一个 可以在需要的地方注入服务,并且更容易 经过测试。

    相反,不要创建一个生活中唯一目的是 存储和返回数据位。

    这不会给$digest 循环(它实现基本的脏检查以测试数据突变)带来过多的负载,这并不是一种奇怪的做事方式。

    编辑:有关性能的更多详细信息,请参阅 Misko(AngularJS 开发人员)在 SO 上的此答案:How does data binding work in AngularJS? 特别注意有关性能的部分。

    【讨论】:

    • 因为他已经拥有该服务,并且它的部分工作也是管理 API(不仅仅是存储和返回),这似乎反对在这种情况下使用 $rootScope。
    • 就个人而言,我更喜欢将数据保留在 $rootScope 之外,但这里是常见问题解答中的认可。因此,既然以这种方式使用 $rootScope 并没有真正的顾虑,这取决于程序员的偏好以及在任何给定应用程序中什么最有意义。
    • 感谢大家的意见!
    • Thanks for 表示最好使用 $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. 什么? - 有人可以翻译吗?
    【解决方案2】:

    为了安抚各方,为什么不直接使用 $cacheFactory。这允许数据请求服务是无状态的,基本上只是一个 getter 和 setter。我承认将数据保存在 $rootScope 上或作为服务中的属性很方便,但感觉不对。使用 $cacheFactory 也很简单。

    首先创建缓存服务:

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

    将 js 文件包含在您的 app.js 中,然后将其注入您的应用声明中:

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

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

    '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, '');
            }
        };
    });
    

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

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

    【讨论】:

    • 嗨布雷克,你的评论真棒!并为所有新手解释细节..这就是服务!我正在我的 AngularJS 自我教程应用程序上尝试它,以便在根范围内拥有这样的缓存服务而不是我的数组。
    • 太棒了!解释得很好!
    • 我刚开始使用AngularJS,想知道,当用户关闭或重新打开应用程序或重新启动手机时,此缓存是否会保留..
    • 您必须将其绑定到本地存储或类似的东西。如果你刚开始 Angular,你应该停下来用 react 代替 ;)
    【解决方案3】:

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

    <div>{{mymodel.property}}</div>
    

    对我来说比

    更具可读性和更短
    <div>{{getPropertyModel()}}</div>
    

    使用javascript

    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 也更好。

    【讨论】:

      【解决方案4】:

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

      其余部分听起来像是一种延迟加载方法。服务是唯一“知道”数据是否已从远程加载并在已缓存的情况下返回并缓存并在未缓存时返回的唯一事物?如果我正确理解了那部分,那么它是一个很好的模式。

      编辑: 这里我对延迟加载采取了类似的方法,请注意缓存只是在服务本身中:

      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}} 对我来说似乎赚了很多。您确切地知道数据的来源,并且不需要做太多的控制器工作。只需告诉服务使用 DataStore.update('endpoint'); 之类的内容更新数据存储。感谢 cmets!
      • 拥有 Angular 意义上的“服务”是多余的,它实际上只是一个实用功能。管理 rootScope 访问。
      • 我喜欢将东西保存在服务中并在个别范围内谨慎地公开它,这样我就不会“疯狂”并开始过度依赖长期存在的“东西”的对象模型在用户界面内。如果每个范围仅包含与手头任务相关的内容,那么如果您需要或想要重构“东西”,您就不太可能遇到麻烦(或更糟的是,工作)。这是一个似乎对我有益的风格选择。此外,当 $rootScope 被消化时,分配给它的任何内容都将进行脏检查——这可能(理论上)会对性能产生可衡量的影响。
      • 在我看来,您应该避免使用 RootScope。使用 RootScope 将使测试变得非常困难。相反,我认为您应该考虑(正如其他人所提到的)将数据放入服务本身。这也将帮助您更轻松地维护代码,因为您将所有代码放在一个地方(服务)。对我来说,听起来你太懒了将服务注入你的控制器,大声笑....(开个玩笑)
      【解决方案5】:

      我只是面临同样的问题,在我看来,将其存储在全局可用的“位置”是正确的方法,但 $rootScope 不是理想的位置。

      我刚刚对此进行了更多研究,而不是将您的数据存储在 $rootScope 上,您可以考虑使用“服务”来管理您的数据/单独的关注点,如下所述(尤其是最后一个代码示例):@987654321 @

      然后在您使用该方法创建的“服务”中,无论您将数据保存在内存、cacheFactory、localstorage(暗指here)和/或您的数据库中(例如通过 AJAX),取决于任何适合您的应用程序的需求。这也意味着可以根据需要独立更改您存储数据的方式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-06
        • 1970-01-01
        • 1970-01-01
        • 2013-11-14
        • 1970-01-01
        • 2013-09-08
        • 2015-09-13
        • 1970-01-01
        相关资源
        最近更新 更多