在 Angularjs 应用程序中使用 $rootscope 的最佳实践?
Best practice for using $rootscope in an Angularjs application?
我们有一个大型 Angularjs 1.6 应用程序,它的 $rootscope 散布在整个应用程序的 200 多个位置,包括过滤器、服务、路由等。因此它需要重构,但我没有确定如何知道何时删除它。什么时候在应用程序中使用 $rootscope 是最佳实践?
我从没读过所有东西,到用它来存储变量,我认为这是为了在控制器之间共享数据。从那以后,我读到最好将 factories/services 用于此用例,并且我还读到一个有效的用例是将 $rootscope 用作全局事件总线。
我并没有真正在 Angularjs 文档中看到这个解释。
来自 ng-book:
When Angular starts to run and generate the view, it will create a binding from the root ng-app
element to the $rootScope. This $rootScope is the eventual parent of all $scope objects.
The $rootScope object is the closest object we have to the global context in an
Angular app. It’s a bad idea to attach too much logic to this global context, in the
same way that it’s not a good idea to dirty the JavaScript global scope.
你说得对,你绝对应该使用 Services 在你的模块之间共享数据和逻辑。
在你的 $rootScope 中放置大量逻辑意味着你的应用程序的可维护性和模块化很差,测试问题也非常困难。
强烈建议您看一看:
- Services AngularJS Documentation
- Thinkster brilliant article on how to share data between controllers
- Screencast by Simpulton
- @Breck421 对 this question
的回答
我知道将所有内容附加到 $rootScope 可能很容易,但很难处理它,只需稍作更改,将您的代码重新用于其他应用程序或模块并总体测试您的应用程序。
编辑
最近我不得不从 API 中获取一些项目并捕获这些项目以便在特定视图中显示它们。项目获取机制在某个 Factory
中,而格式化和显示项目的机制在 Controller
.
中
所以,我不得不在 Factory
中发出一个事件,当项目被获取并在 Controller
中捕获这个事件。
$rootScope 方式
//Factory
$rootScope.$broadcast('refreshItems', items);
//Controller
$scope.$on('refreshItems', doSomething());
它显然有效,但我不太喜欢使用 $rootScope
而且我还注意到该任务的性能非常糟糕。
然后我试了一下 Postal.js:
Postal.js is an in-memory message bus - very loosely inspired by AMQP -
written in JavaScript. Postal.js runs in the browser, or on the server
using node.js. It takes the familiar "eventing-style" paradigm (of
which most JavaScript developers are familiar) and extends it by
providing "broker" and subscriber implementations which are more
sophisticated than what you typically find in simple event
emitting/aggregation.
我尝试使用 Postal.js
来满足这种需求,我发现它确实比使用 $rootScope
快。
//Factory
$scope.$bus.publish({
channel : 'reloadItems',
topic : 'reloadItems'
data : items
);
//Controller
$scope.$bus.subscribe({
channel : 'reloadItems',
topic : 'reloadItems',
callback : function () {
resetAndLoadItems();
}
});
希望对您有所帮助。
来自Angluar docs:每个应用程序都有一个根范围。所有其他范围都是根范围的后代范围。 作用域通过观察模型变化的机制提供模型和视图之间的分离。
当然,这要归结为意见和风格问题。我倾向于遵循非常接近 John Papa's Angular Style Guide 的风格。
为了与两者保持一致,并遵循良好的关注点分离策略,我的体系结构包含跨应用程序共享的工厂模型。我的控制器又都绑定到保存共享数据的服务。
使用 $rootScope 作为全局事件总线正是 Angular 使用它的方式。你应该跟着一起做吗?我不明白为什么不。但如果你是,请确保明确定义目的,甚至可以使用你自己的服务将事件注册到全局事件总线。这样你就可以将你的应用程序与 Angular 解耦,如果你决定要更改全局事件总线所在的框架,那么你可以在一个地方更改它。
这就是我的建议:
全局事件总线
// Angular specific: add service to module
angular.module('app').factory('globalEventBus', GlobalEventBus);
// Angular specific: inject dependencies
GlobalEventBus.$inject(['$rootScope']);
// Non framework specific.
// param: fameworkEventBus will be $rootScope once injected
function GlobalEventBus(fameworkEventBus) {
var globalEventBus = this;
globalEventBus.registerEvent(params...){
fameworkEventBus.
}
return globalEventBus;
}
全局数据模型
我的数据模型很聪明,往往包含提供有关自身或 retrieve/return 特定数据信息的函数。
// Angular specific: add service to module
angular.module('app').factory('dataModel', DataModel);
function DataModel() {
var dataModel= this;
dataModel.myData = {};
dataModel.GetSpecificData = funtion(param){
return ...
}
return dataModel;
}
控制器
// Angular specific
angular.module('app').controller('MyController', MyController);
// Angular specific: inject dependencies to controller
MyController.$inject = ['dataModel'];
// By convention I use the same parameter name as the service.
// It helps me see quickly if my order of injection is correct
function MyController(dataModel) {
var myController = this;
// Bind to the service itself, and NOT to the service data property
myController.myData = dataModel;
myController.doStuff = function(){
}
}
Here 很有趣 post 关于绑定到服务而不是服务属性。
总而言之,您必须判断什么最适合您。良好的系统架构和良好的风格为我节省了无数时间来解决完全可以避免的问题。
在使用 Angular 进行更多工作和更多阅读之后,我发现了使用 $rootscope 的基本经验法则,我想将其添加到其他答案中:
Only add properties that are static or constant. Anything else that
represents a changing state or a mutable value should have a
corresponding directive or controller to handle it.
我们有一个大型 Angularjs 1.6 应用程序,它的 $rootscope 散布在整个应用程序的 200 多个位置,包括过滤器、服务、路由等。因此它需要重构,但我没有确定如何知道何时删除它。什么时候在应用程序中使用 $rootscope 是最佳实践?
我从没读过所有东西,到用它来存储变量,我认为这是为了在控制器之间共享数据。从那以后,我读到最好将 factories/services 用于此用例,并且我还读到一个有效的用例是将 $rootscope 用作全局事件总线。
我并没有真正在 Angularjs 文档中看到这个解释。
来自 ng-book:
When Angular starts to run and generate the view, it will create a binding from the root ng-app element to the $rootScope. This $rootScope is the eventual parent of all $scope objects. The $rootScope object is the closest object we have to the global context in an Angular app. It’s a bad idea to attach too much logic to this global context, in the same way that it’s not a good idea to dirty the JavaScript global scope.
你说得对,你绝对应该使用 Services 在你的模块之间共享数据和逻辑。
在你的 $rootScope 中放置大量逻辑意味着你的应用程序的可维护性和模块化很差,测试问题也非常困难。
强烈建议您看一看:
- Services AngularJS Documentation
- Thinkster brilliant article on how to share data between controllers
- Screencast by Simpulton
- @Breck421 对 this question 的回答
我知道将所有内容附加到 $rootScope 可能很容易,但很难处理它,只需稍作更改,将您的代码重新用于其他应用程序或模块并总体测试您的应用程序。
编辑
最近我不得不从 API 中获取一些项目并捕获这些项目以便在特定视图中显示它们。项目获取机制在某个 Factory
中,而格式化和显示项目的机制在 Controller
.
所以,我不得不在 Factory
中发出一个事件,当项目被获取并在 Controller
中捕获这个事件。
$rootScope 方式
//Factory
$rootScope.$broadcast('refreshItems', items);
//Controller
$scope.$on('refreshItems', doSomething());
它显然有效,但我不太喜欢使用 $rootScope
而且我还注意到该任务的性能非常糟糕。
然后我试了一下 Postal.js:
Postal.js is an in-memory message bus - very loosely inspired by AMQP - written in JavaScript. Postal.js runs in the browser, or on the server using node.js. It takes the familiar "eventing-style" paradigm (of which most JavaScript developers are familiar) and extends it by providing "broker" and subscriber implementations which are more sophisticated than what you typically find in simple event emitting/aggregation.
我尝试使用 Postal.js
来满足这种需求,我发现它确实比使用 $rootScope
快。
//Factory
$scope.$bus.publish({
channel : 'reloadItems',
topic : 'reloadItems'
data : items
);
//Controller
$scope.$bus.subscribe({
channel : 'reloadItems',
topic : 'reloadItems',
callback : function () {
resetAndLoadItems();
}
});
希望对您有所帮助。
来自Angluar docs:每个应用程序都有一个根范围。所有其他范围都是根范围的后代范围。 作用域通过观察模型变化的机制提供模型和视图之间的分离。
当然,这要归结为意见和风格问题。我倾向于遵循非常接近 John Papa's Angular Style Guide 的风格。
为了与两者保持一致,并遵循良好的关注点分离策略,我的体系结构包含跨应用程序共享的工厂模型。我的控制器又都绑定到保存共享数据的服务。
使用 $rootScope 作为全局事件总线正是 Angular 使用它的方式。你应该跟着一起做吗?我不明白为什么不。但如果你是,请确保明确定义目的,甚至可以使用你自己的服务将事件注册到全局事件总线。这样你就可以将你的应用程序与 Angular 解耦,如果你决定要更改全局事件总线所在的框架,那么你可以在一个地方更改它。
这就是我的建议:
全局事件总线
// Angular specific: add service to module
angular.module('app').factory('globalEventBus', GlobalEventBus);
// Angular specific: inject dependencies
GlobalEventBus.$inject(['$rootScope']);
// Non framework specific.
// param: fameworkEventBus will be $rootScope once injected
function GlobalEventBus(fameworkEventBus) {
var globalEventBus = this;
globalEventBus.registerEvent(params...){
fameworkEventBus.
}
return globalEventBus;
}
全局数据模型
我的数据模型很聪明,往往包含提供有关自身或 retrieve/return 特定数据信息的函数。
// Angular specific: add service to module
angular.module('app').factory('dataModel', DataModel);
function DataModel() {
var dataModel= this;
dataModel.myData = {};
dataModel.GetSpecificData = funtion(param){
return ...
}
return dataModel;
}
控制器
// Angular specific
angular.module('app').controller('MyController', MyController);
// Angular specific: inject dependencies to controller
MyController.$inject = ['dataModel'];
// By convention I use the same parameter name as the service.
// It helps me see quickly if my order of injection is correct
function MyController(dataModel) {
var myController = this;
// Bind to the service itself, and NOT to the service data property
myController.myData = dataModel;
myController.doStuff = function(){
}
}
Here 很有趣 post 关于绑定到服务而不是服务属性。
总而言之,您必须判断什么最适合您。良好的系统架构和良好的风格为我节省了无数时间来解决完全可以避免的问题。
在使用 Angular 进行更多工作和更多阅读之后,我发现了使用 $rootscope 的基本经验法则,我想将其添加到其他答案中:
Only add properties that are static or constant. Anything else that represents a changing state or a mutable value should have a corresponding directive or controller to handle it.