在 Angularjs 应用程序中使用 $rootscope 的最佳实践



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

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

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

来自 ng-book:

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

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

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

我强烈建议您看一下:

  • 服务 AngularJS 文档
  • Thinkster 关于如何在控制器之间共享数据的精彩文章
  • 辛普尔顿截屏
  • @Breck421 对这个问题的回答

我知道将所有内容附加到$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 在浏览器或服务器上运行 使用节点.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();
  }
});

我希望我对您有所帮助。

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

当然,这将归结为意见和风格问题。我倾向于遵循一种非常接近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(){
  }
}

这是一篇关于绑定到服务而不是服务属性的有趣文章。

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

在使用 Angular 做了更多的工作和更多的阅读之后,我发现了使用我想添加到其他答案中的$rootscope的基本经验法则:

仅添加静态或常量属性。 其他任何内容 表示不断变化的状态或可变值应具有 相应的指令或控制器来处理它。

最新更新