7

我们有一个大型 Angularjs 1.6 应用程序,它的 $rootscope 分散在整个应用程序中,在过滤器、服务、路由等的 200 多个位置。所以它需要重构,但我不知道如何知道何时删除它。什么时候在应用程序中使用 $rootscope 是最佳实践?

我已经阅读了所有内容,从从来没有,到使用它来存储变量,我认为这是在控制器之间共享数据。从那以后我读到最好为这个用例使用工厂/服务,我还读到一个有效的用例是使用 $rootscope 作为全局事件总线。

我并没有在 Angularjs 文档中真正看到这一点。

4

3 回答 3

10

来自ng-book

当 Angular 开始运行并生成视图时,它会创建一个从根 ng-app 元素到 $rootScope 的绑定。这个 $rootScope 是所有 $scope 对象的最终父级。$rootScope 对象是我们在 Angular 应用程序中最接近全局上下文的对象。将过多的逻辑附加到这个全局上下文是一个坏主意,就像弄脏 JavaScript 全局范围不是一个好主意一样。

你是对的,你绝对应该使用服务在你的模块之间共享数据和逻辑。

在您的$rootScope中放置大量逻辑意味着您的应用程序的可维护性和模块化程度很差,测试问题也非常困难。

我强烈建议你看看:

我知道将所有内容附加到$rootScope可能很容易,但是很难对其进行处理,进行少量更改,将代码重用于其他应用程序或模块并测试您的应用程序。

编辑

最近我不得不从 API 中获取一些项目并捕获这些项目,以便在某个视图中显示它们。项目获取机制是在一定的Factory,而格式化和显示项目的机制是在Controller

Factory因此,我必须在获取项目时发出一个事件,并在Controller.

$rootScope 方式

//Factory
$rootScope.$broadcast('refreshItems', items);
//Controller
$scope.$on('refreshItems', doSomething());

它显然有效,但我真的不喜欢使用$rootScope,而且我还注意到该任务的性能非常糟糕。

然后我尝试给Postal.js一个镜头:

Postal.js 是一个内存消息总线——非常松散地受到 AMQP 的启发——用 JavaScript 编写。Postal.js 在浏览器中运行,或者使用 node.js 在服务器上运行。它采用熟悉的“事件风格”范例(大多数 JavaScript 开发人员都熟悉),并通过提供“代理”和订阅者实现来扩展它,这些实现比您通常在简单的事件发射/聚合中找到的更复杂。

我尝试使用Postal.js这种需求,我发现它确实比$rootScope用于此目的更快。

//Factory
$scope.$bus.publish({
                  channel : 'reloadItems',
                  topic   : 'reloadItems'
                  data    : items
);

//Controller
$scope.$bus.subscribe({
  channel  : 'reloadItems',
  topic    : 'reloadItems',
  callback : function () {
    resetAndLoadItems();
  }
});

我希望我对您有所帮助。

于 2016-02-11T17:27:45.480 回答
3

来自Angular 文档:每个应用程序都有一个根范围。所有其他作用域都是根作用域的后代作用域。范围通过观察模型变化的机制提供模型和视图之间的分离。

当然,这将归结为意见和风格问题。我倾向于遵循非常接近John Papa 的 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;
}

全局数据模型

我的数据模型很智能,并且倾向于包含提供有关自身信息或检索/返回特定数据的功能。

// 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(){
  }
}

是一篇关于绑定到服务而不是服务属性的有趣帖子。

总而言之,您必须判断什么对您最有效。良好的系统架构和良好的风格为我节省了无数时间来解决完全可以避免的问题。

于 2016-02-11T17:48:47.070 回答
1

在使用 Angular 进行更多工作并阅读更多内容后,我发现了使用 $rootscope 的基本经验法则,我想将其添加到其他答案中:

仅添加静态或常量的属性。任何其他表示变化状态或可变值的东西都应该有相应的指令或控制器来处理它。

于 2016-03-31T20:45:08.270 回答