为什么不建议将 $rootScope 与函数一起使用?

IT技术 javascript angularjs rootscope
2021-02-21 21:07:37

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

$rootScope 存在,但可用于作恶

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

有时,您希望将一些数据全局化到整个应用程序。对于这些,您可以$rootScope像任何其他范围一样在其上注入和设置值。由于范围从根范围继承,因此这些值将可用于附加到指令的表达式,就像ng-show本地$scope.

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

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

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

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

3个回答

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

$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 的module中使用常量和值,但这不是一个很好的描述。

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

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

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

@razorblade 是的,你是对的。有一个根范围提供程序,但这只是让您调整摘要限制。
2021-04-16 21:07:37
“$rootScope 是 angular.config()' 期间唯一可用的范围”?如果我没记错的话,我无法在配置阶段访问 $rootScope,我很确定这个配置 bloque 仅适用于提供者。你男人 .run() bloque 吗?如果我错了,请纠正我。
2021-04-25 21:07:37
我想我知道作者将如何使用仅存储和返回数据位的服务。我最近建议一个团队摆脱 $rootScope 上的变量。有更好的方法来实现他们想要的功能。他们通过将它们移到具有 getter 和 setter 的服务中解决了这个问题。显然,他们刚刚转移了他们的全球状态问题。
2021-05-01 21:07:37

全局变量被滥用

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

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

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

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

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

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

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

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

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

在 Angular 中共享数据

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

如果您引用整个部分,则应考虑引用原始内容。c2.com/cgi/wiki?GlobalVariablesAreBad
2021-04-17 21:07:37
抱歉忘记了,现在补上
2021-04-21 21:07:37

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

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

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

命名空间

您正在开发 Facebook API,其他人正在开发 Twitter API。你们都认为使用$rootScopefor 函数是个好主意,都写了一个$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同样舒适的方式测试您分配给的内容。

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