12

在研究 Angularjs 的 FEQ 时,我看到了下面的文章:

$rootScope 存在,但可用于作恶

Angular 中的作用域形成一个层次结构,原型继承自树顶部的根作用域。通常可以忽略这一点,因为大多数视图都有自己的控制器,因此也有自己的范围。

有时,您想让某些数据对整个应用程序具有全局性。$rootScope对于这些,您可以像任何其他范围一样在其上注入和设置值。由于作用域从根作用域继承,这些值将可用于附加到指令的表达式,就像ng-show您的 local 上的值一样$scope

当然,全局状态很糟糕,您应该$rootScope谨慎使用,就像(希望)在任何语言中使用全局变量一样。特别是,不要将其用于代码,仅用于数据。如果您想在 上放置一个函数$rootScope,最好将它放在一个可以在需要的地方注入并且更容易测试的服务中。

相反,不要创建一个生活中唯一目的是存储和返回数据位的服务。

— AngularJS 常见问题 - $rootScope 存在,但它可以用于作恶

所以我的疑问是为什么不建议将 $rootScope 用作全局函数?有没有性能问题?

4

3 回答 3

10

我过去已经回答过这个问题,但你问这些问题很好。

$rootScope 存在,但它可以用于 Angular 中的邪恶作用域,形成层次结构,原型继承自树顶部的根作用域。通常可以忽略这一点,因为大多数视图都有自己的控制器,因此也有自己的范围。

非隔离范围是分层的,但大多数开发人员应该使用具有隔离范围的指令。AngularJS 范围的分层特性是 Angular 应用程序中许多错误的根源。这是一个我喜欢称之为作用域出血的问题,即作用域属性在 DOM 树的某个地方被神奇地修改了,而你不知道为什么。

Angular 的默认行为是固有范围,这使得一个控制器很容易更新由另一个控制器管理的东西,等等。这就是在源代码之间创建意大利面条式连接的方式。使维护该代码变得非常困难。

有时,您想让某些数据对整个应用程序具有全局性。对于这些,您可以像任何其他范围一样注入 $rootScope 并在其上设置值。

不,这是不正确的。AngularJS 允许你定义诸如常量、值和服务之类的东西。这些是可以注入到路由、控制器和指令中的东西。这就是你如何让你的应用程序可以全局访问东西,如果你想让你的控制器或指令可测试,你就是这样做的。单元测试编写者不知道指令或控制器所依赖的 $rootScope 中应该包含哪些属性。他们必须假设 $rootScope 没有变异以提供服务或数据。

当然,全局状态很糟糕,您应该谨慎使用 $rootScope,就像(希望)在任何语言中使用全局变量一样。

问题不是 $rootScope 而是人们用它做什么。许多应用程序将当前用户、身份验证令牌和会话数据添加到 rootScope。这最终会在模板中大量使用(如果用户登录则显示 X,否则显示 Y)。问题是 HTML 不传达作用域层次结构。所以当你看到{{user.firstname + ' ' + user.lastname}}你不知道变量user是从哪里来的。第二个问题是子作用域可以影响根属性。与前面的示例一样,如果指令执行此操作scope.user = 'bla bla bla'。它没有替换 rootScope 上的值。它隐藏了它。现在你在模板中得到了一些奇怪的意想不到的东西,你不知道为什么变量user会改变。

相反,不要创建一个生活中唯一目的是存储和返回数据位的服务。

Angular$cacheFactory$templateCache是仅存在存储数据的服务示例。我认为作者试图鼓励在 Angular 的模块中使用常量和值,但这并不是一个好的描述。

所以我的疑问是为什么不建议将 $rootScope 用作全局函数?有没有性能问题?

$rootScope 是angular.config(..). 如果这是您唯一可以修改的时间,则可以在此期间修改范围。例如; 您可能需要在应用启动之前注入 API 密钥或 Google 分析变量。

任何范围内的函数通常都是一个坏主意。主要是因为作用域中的所有内容都在模板上的表达式中被消化。隐藏繁重操作的功能。调用函数时无法通过读取 HTML 来判断模板的重量。我见过范围函数getHeight(),比如函数本身执行 3 级嵌套循环的地方。每次 Angular 消化观察者以查看它是否已更改时,都必须调用该函数。您应该尽量保持模板干燥

于 2015-09-24T14:08:13.163 回答
3

全局变量被滥用

$rootScope几乎是一个全局变量,并且有它的位置,但肯定会被大多数使用它的人滥用。这些是一般不应使用全局变量的原因。

非局部性——当单个元素的范围有限时,源代码最容易理解。全局变量可以被程序的任何部分读取或修改,因此很难记住或推理每种可能的用途。

没有访问控制或约束检查——程序的任何部分都可以获取或设置全局变量,并且任何关于其使用的规则都可以很容易地被破坏或遗忘。(换句话说,get/set 访问器通常比直接数据访问更可取,对于全局数据更是如此。)通过扩展,在您可能希望运行不受信任的代码的情况下,缺乏访问控制极大地阻碍了实现安全性(例如使用 3rd 方插件)。

隐式耦合——具有许多全局变量的程序通常在其中一些变量之间存在紧密耦合,以及变量和函数之间的耦合。将耦合的项目分组为有凝聚力的单元通常会导致更好的程序。

并发问题——如果全局变量可以被多个执行线程访问,同步是必要的(而且经常被忽略)。当动态链接模块与全局变量时,即使在几十个不同上下文中测试的两个独立模块是安全的,组合系统也可能不是线程安全的。

命名空间污染——全局名称随处可见。当您认为您正在使用本地(通过拼写错误或忘记声明本地)时,您可能会在不知不觉中最终使用全局,反之亦然。此外,如果您必须将具有相同全局变量名称的模块链接在一起,如果幸运的话,您会遇到链接错误。如果你不走运,链接器将简单地将所有使用相同名称的对象视为同一个对象。

内存分配问题——某些环境的内存分配方案使得全局变量的分配变得棘手。在“构造函数”具有除分配之外的副作用的语言中尤其如此(因为在这种情况下,您可以表达两个全局变量相互依赖的不安全情况)。此外,当动态链接模块时,可能不清楚不同的库是否有自己的全局实例或全局是否共享。

测试和限制- 使用全局变量的源代码更难测试,因为无法在运行之间轻松设置“干净”环境。更一般地,利用未明确提供给该源的任何类型的全局服务(例如读取和写入文件或数据库)的源由于相同的原因而难以测试。对于通信系统,测试系统不变量的能力可能需要同时运行一个系统的多个“副本”,这会受到任何共享服务(包括全局内存)的使用的极大阻碍,这些共享服务并未作为测试的一部分提供共享.

来源http ://c2.com/cgi/wiki?GlobalVariablesAreBad

在 Angular 中共享数据

在 Angular 中跨控制器共享数据时,您应该使用服务。使用您的自定义服务,您可以创建一个 getter 和一个 setter 方法。您将它注入到您需要的控制器中,并可以在您的应用程序中使用它。

于 2015-09-24T12:55:30.160 回答
1

不存在性能问题。它实际上会在一小部分时间内提高您的性能,因为您不需要依赖注入大量服务。

但这是设计的一个大问题。考虑一个具有数十个视图、复杂组件并与许多众所周知的 API(例如 Twitter、Flickr、Facebook、OAuth 等)相关联的大型应用程序。

您不会单独开发此应用程序。会出现以下问题:

命名空间

您正在使用 Facebook API,其他人正在使用 Twitter API。你们都认为使用$rootScope函数是一个好主意,并且都编写了一个$rootScope.login函数。这样做时你会如何解决这个问题git merge?你需要命名空间,唉,你需要开发两个服务myFacebookAPImyTwitterAPI然后可以实现相同的登录接口。(login(user,pw))。请注意,当您可以执行以下操作时,这使您能够抽象出您在控制器中处理的实际社交网络:

$scope.callAction = function (action) {
var service;
    if ($scope.serviceSelected === 'fb') {
         service = myFacebookAPI;
    } else {
         service = myTwitterAPI;
    }
    service[action]();
};

测试

在专业开发时,您编写测试。Angular 为您提供了对服务等进行自动化测试的工具,但您无法以$rootScope同样舒适的方式测试分配给您的东西。

其他问题也会出现,但我认为这应该足以让您自己考虑。

于 2015-09-24T13:32:40.147 回答