小编典典

AngularJS中控制器之间通信的正确方法是什么?

all

控制器之间通信的正确方法是什么?

我目前正在使用一种可怕的软糖,涉及window

function StockSubgroupCtrl($scope, $http) {
    $scope.subgroups = [];
    $scope.handleSubgroupsLoaded = function(data, status) {
        $scope.subgroups = data;
    }
    $scope.fetch = function(prod_grp) {
        $http.get('/api/stock/groups/' + prod_grp + '/subgroups/').success($scope.handleSubgroupsLoaded);
    }
    window.fetchStockSubgroups = $scope.fetch;
}

function StockGroupCtrl($scope, $http) {
    ...
    $scope.select = function(prod_grp) {
        $scope.selectedGroup = prod_grp;
        window.fetchStockSubgroups(prod_grp);
    }
}

阅读 153

收藏
2022-03-14

共1个答案

小编典典

编辑 :此答案中解决的问题已在 angular.js版本
1.2.7
中得到解决。$broadcast现在避免在未注册的范围上冒泡,并且运行速度与 $emit 一样快。
$broadcast 性能与 angular 1.2.16 的 $emit 相同

所以,现在你可以:

  • $broadcast从使用$rootScope
  • 从需要了解事件$on 的当地人$scope那里收听

原答案如下

我强烈建议不要使用$rootScope.$broadcast+$scope.$on而是使用$rootScope.$emit+
$rootScope.$on。前者可能会导致@numan 提出的严重性能问题。那是因为事件将在 所有 范围内冒泡。

但是,后者(使用$rootScope.$emit+ $rootScope.$on不受 此影响,因此可以用作快速通信通道!

从角度文档$emit

通过范围层次结构向上调度事件名称,通知已注册的

由于没有上述范围$rootScope,因此不会发生冒泡。$rootScope.$emit()将/$rootScope.$on()用作
EventBus是完全安全的。

但是,在 Controller
中使用它时有一个问题。如果您直接从控制器中绑定到,则当您的本地被破坏$rootScope.$on()时,您必须自己清理绑定。$scope这是因为控制器(与服务相比)可以在应用程序的生命周期内多次实例化,这将导致绑定最终在整个地方造成内存泄漏:)

要取消注册,只需监听您$scope$destroy事件,然后调用返回的函数$rootScope.$on

angular
    .module('MyApp')
    .controller('MyController', ['$scope', '$rootScope', function MyController($scope, $rootScope) {

            var unbind = $rootScope.$on('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });

            $scope.$on('$destroy', unbind);
        }
    ]);

我想说,这并不是一个真正的角度特定的事情,因为它也适用于其他 EventBus 实现,你必须清理资源。

但是,对于这些情况,您 可以 让您的生活更轻松。例如,您可以猴子补丁$rootScope并给它一个$onRootScope订阅在
上发出的事件,但也可以在本地被破坏$rootScope时直接清理处理程序。$scope

猴子修补$rootScope提供这种$onRootScope方法的最干净的方法是通过装饰器(运行块可能也可以做到这一点,但是pssst,不要告诉任何人)

为了确保$onRootScope在枚举时属性不会出现意外,$scope我们使用Object.defineProperty()并设置enumerablefalse.
请记住,您可能需要 ES5 垫片。

angular
    .module('MyApp')
    .config(['$provide', function($provide){
        $provide.decorator('$rootScope', ['$delegate', function($delegate){

            Object.defineProperty($delegate.constructor.prototype, '$onRootScope', {
                value: function(name, listener){
                    var unsubscribe = $delegate.$on(name, listener);
                    this.$on('$destroy', unsubscribe);

                    return unsubscribe;
                },
                enumerable: false
            });


            return $delegate;
        }]);
    }]);

使用这种方法,上面的控制器代码可以简化为:

angular
    .module('MyApp')
    .controller('MyController', ['$scope', function MyController($scope) {

            $scope.$onRootScope('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });
        }
    ]);

因此,作为所有这一切的最终结果,我强烈建议您使用$rootScope.$emit+ $scope.$onRootScope

顺便说一句,我正试图说服 Angular 团队解决 Angular 核心中的问题。这里正在进行讨论:https
://github.com/angular/angular.js/issues/4574

这是一个 jsperf,它显示了$broadcast在只有 100 个的体面场景中性能影响有多大$scope

http://jsperf.com/rootscope-emit-vs-rootscope-
broadcast

jsperf 结果

2022-03-14