AngularJS中的$http与service与ngResource比较

42
我想了解使用简单的$http向服务器发送请求和/或将该请求封装在服务中与使用ngResource对象(除了与RESTful资源相关的明显优势之外)的优缺点。
据我所知,$http请求是低级别的,但非常灵活和可配置的,而在处理RESTful API时,ngResource对象使通信变得非常简单。
我想询问的是,在非常简单的场景下,例如从服务器检索数据(获取一组对象请求),是使用$http请求更有效还是将其封装在服务中(是否总是如此?)或者使用ngResource对象?
任何想法都将不胜感激。例如,$http响应可以被缓存,但是ngResource可以吗?

3
两种方式都可以被缓存,你在问题中指出了使用其中一种的原因。如果你有一个RESTful接口,那么使用$resource会更好,因为你会写更少的样板代码,这对于RESTful接口是常见的。如果你不使用RESTful服务,则$http更有意义。你可以以任何一种方式缓存数据,参考http://pseudobry.com/power-up-%24http.html。如果这回答了你的问题,请让我知道,我会把它发布为答案并删除评论。 - shaunhusain
1
谢谢回复,我猜它确实有用,我只是想知道使用$http和ngResource的优缺点,特别是在上述边缘情况下,您仍然返回对象,可能来自RESTful API,但不需要进一步交互,只需GET。也许我读得太多了,只是找不到讨论用例,只有实现。此外,有些讨论建议将$http请求包装在服务中,但没有找到明确的最佳实践。 - adamK
3
我认为将$http请求放入服务中通常更好,因为您希望从多个位置访问数据,并且服务作为单例,因此您可以在那里处理任何类型的缓存,控制器可以监视适当的服务以更新其自己的数据。我发现,在控制器中使用$watch来监视服务上的数据并从我的服务方法返回promise组合,可以为我提供最大的灵活性,以便在控制器中更新内容。 - shaunhusain
没问题,很高兴你理解了,但是一定要看我下面链接的视频,它非常有价值。 - shaunhusain
你说得没错,非常有启发性!谢谢。 - adamK
显示剩余2条评论
2个回答

42

我决定将这个问题转化为一个答案,因为在评论中我们已经基本弄清楚了你想知道的内容:

使用$http或$resource仍然可以缓存结果,你在问题中指出了使用其中一个的原因。如果你有一个RESTful接口,那么使用$resource更好,因为你会编写少量常见于RESTful接口的样板代码,如果你不使用RESTful服务,则$http更合适。你可以以任何一种方式缓存数据http://www.pseudobry.com/power-up-http-with-caching/

我认为将$http或$resource请求放入服务中通常会更好,因为你希望从多个位置访问数据,而服务充当单例。因此,你可以在那里处理任何你想要做的缓存,并且控制器只需要监视适当的服务以更新它们自己的数据。我发现,在我的服务方法中结合使用控制器中的$watch和返回承诺,可以使我在如何更新控制器中的事物方面具有最大的灵活性。

在控制器定义的顶部注入exampleService,我会像这样放置一些东西。

angular.module("exampleApp", []).service('exampleService', ["$http", "$q" ,function ($http, $q) {
    var service = {
        returnedData: [],
        dataLoaded:{},
        getData = function(forceRefresh)
        {
            var deferred = $q.defer();

            if(!service.dataLoaded.genericData || forceRefresh)
            {
                $http.get("php/getSomeData.php").success(function(data){
                    //service.returnedData = data;
                    //As Mark mentions in the comments below the line above could be replaced by
                    angular.copy(data, service.returnedData);
                    //if the intention of the watch is just to update the data
                    //in which case the watch is unnecessary and data can
                    //be passed directly from the service to the controller
                    service.dataLoaded.genericData = true;
                    deferred.resolve(service.returnedData);
                });
            }
            else
            {
                deferred.resolve(service.returnedData);
            }

            return deferred.promise;
        },
        addSomeData:function(someDataToAdd)
        {
            $http.post("php/addSomeData.php", someDataToAdd).success(function(data){
                service.getData(true);
            });
        }
    };
    service.getData();
    return service;
}]).controller("ExampleCtrl", ["$scope", "exampleService", function($scope, exampleService){
  //$scope.$watch(function() {return exampleService.returnedData}, function(returnedData){
  //  $scope.myModel.someData = returnedData;
  //});
  //if not using angular.copy() in service just use watch above
  $scope.myModel.someData = exampleService.returnedData;
}]);

这里还有一个来自Angular团队的关于最佳实践的不错视频,我现在还在反复观看并逐渐吸收其中的内容。

http://www.youtube.com/watch?v=ZhfUv0spHCY

具体来讲是关于服务(services)和控制器(controllers)的区别: http://www.youtube.com/watch?v=ZhfUv0spHCY&t=26m41s


13
如果您在服务中使用angular.copy(data, service.returnedData),则可以消除控制器中的$watch。这样,您将更新同一个数组,而不是分配一个新的数组。有一个有效的plunker,请参见https://dev59.com/TmQm5IYBdhLWcg3wswpv - Mark Rajcok

5

有一个比它们是否可以被缓存更有意义的区别。

使用资源将消除在服务或返回的数据上设置$watches的需要。您也不必完全使用promises。基本上,它消除了在shaunhusain在他的示例中所做的任何操作。

对资源方法的调用会返回与该资源相关联的结构的空实例,并且您可以并且应该直接绑定到它。同一实例将在稍后填充数据。由于您已绑定到实例,因此当它填充时,您的显示将自动更新。

资源还可以提供一种封装的方式来转换它提供的服务的请求和响应,使其对资源的客户端不可见。

资源就像是增强版的服务。


刚注意到这个回答。 我对此持不同意见。 如果您只使用angular.copy替换对象的内容而不是对象本身,则$watches在任何情况下都是无关紧要的。 服务允许您定义一个可注入的对象,该对象是单例,我并不认为它有更多或更少。 $ resource只是在$ http上面抽象出来处理RESTful事物,您最终会一次又一次地重写$ http。 承诺公开请求的异步性质,并且也可以使用资源上的$promise获得(这是一件好事)。 - shaunhusain
我有时会从服务器收到各种错误的场景 - 与表单字段相关的输入或业务验证错误,或一般的业务错误,或者“此记录已被其他人编辑,请重新加载”,或用户因为不活动而过期的会话,或意外的服务器错误。其中一些错误返回为HTTP 500或422或401或“error[key]”,具体取决于错误类型。中央$http包装器可使在一个地方轻松处理所有这些错误。如果使用$resource,我不确定如何处理这些错误。 - JustAMartin
@JustAMartin 即使您使用 $resource,它在内部也是基于 $http 工作的,因此您可以全局拦截所有服务器请求和响应,并可以全局处理错误。 - Mukun
@JustAMartin 你不需要牺牲 $resource,你可以只使用 HTTP 请求/响应拦截器 https://djds4rce.wordpress.com/2013/08/13/understanding-angular-http-interceptors/ - Toolkit
对我来说,这似乎是一个更好的解决方案,因为我一直讨厌服务,因为我必须设置监视器,而我更喜欢使用rootScope。事实上,我可以直接分配资源也是一个好处。 - Toolkit
还是不明白为什么人们在服务数据上使用 $watches。只需绑定到控制器中创建的未填充、未定义的变量即可。使用未定义值的表单元素在数据到达之前不会显示,此时表单将变为可见状态。无需观察数据,双向绑定已经在观察了。 - Rodney P. Barbati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接