编辑:此答案中解决的问题已在 angular.js version 1.2.7 中得到解决。 $broadcast 现在可以避免在未注册的作用域上冒泡,并且运行速度与 $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 是完全安全的。
但是,在控制器中使用它时有一个问题。如果您从控制器中直接绑定到$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() 并将enumerable 设置为false。请记住,您可能需要 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,它显示了在只有 100 个 $scope 的体面场景中,$broadcastbrings 的性能影响有多大。
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast