Why using $rootScope with functions is not recommended?

跟風遠走 提交于 2019-11-26 23:11:41

I've answered this in the past, but it's good that you're asking these questions.

$rootScope exists, but it can be used for evil Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree. Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.

Non-isolated scopes are hierarchical, but most developers should be using directives that have isolated scopes. The very hierarchical nature of AngularJS's scope is the source of many bugs in angular apps. It's a problem I like to call scope bleeding where a scope property is modified magically somewhere in the DOM tree and you don't know why.

Angular's default behavior is to inherent scopes and this makes it tempting for one controller to update something managed by another controller, so on, and so on. This is how spaghetti connections between source code is created. Making it very difficult to maintain that code.

Occasionally there are pieces of data that you want to make global to the whole app. For these, you can inject $rootScope and set values on it like any other scope.

No that's not correct. AngularJS allows you to define things like constants, values, and services. These are things that can be injected into routes, controllers and directives. That is how you make things accessible globally to your app, and this how you do it if you want to make your controllers or directives testable. A unit test writer doesn't know what properties should be in the $rootScope that a directive or controller depends upon. They have to assume that the $rootScope has not mutated to provide a service or data.

Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language.

The problem isn't $rootScope but what people are doing with it. Many apps add the current user, the auth tokens, and the session data into the rootScope. This ends up getting used heavily in templates (shows X if user logged in otherwise show Y). The problem is that the HTML doesn't communicate scope hierarchy. So when you see {{user.firstname + ' ' + user.lastname}} you have no idea where the variable user came from. The second problem is child scopes can shadow root properties. As in the previous example if a directive does this scope.user = 'bla bla bla'. It hasn't replaced the value on the rootScope. It's hidden it. Now you get some strange unexpected things in the templates, and you don't know why the variable user has changed.

Conversely, don't create a service whose only purpose in life is to store and return bits of data.

Angular's $cacheFactory and $templateCache are examples of services that exist only too store data. I think the author was trying to encourage the use of constants and values in Angular's modules, but that's not a good description to do that.

So My doubt is why $rootScope is not recommended for functions as a global function? Is there any performance issue?

The $rootScope is the only scope available during angular.config(..). It's during this time that the scope can be modified if this is the only time that you can do it. For example; you may need to inject an API key or Google anayltics variable before the app starts.

Functions on any scope are generally a bad idea. Mainly for the reason that everything in scopes is digested in expressions on the templates. Functions tent to hide heavy operations. It's impossible to tell how heavy a template is by reading the HTML when it calls a function. I've seen scope functions like getHeight() where the function itself performed 3 levels of nested loops. That function has to get called every time angular digests the watchers to see if it's changed. You should try to keep your templates as dry as possible.

Global Variables are Abused

$rootScope is pretty much a global variable and has its place but is definitely abused by most of the people that use it. These are the reasons Globals in general should not be used.

Non-locality -- Source code is easiest to understand when the scope of its individual elements are limited. Global variables can be read or modified by any part of the program, making it difficult to remember or reason about every possible use.

No Access Control or Constraint Checking -- A global variable can be get or set by any part of the program, and any rules regarding its use can be easily broken or forgotten. (In other words, get/set accessors are generally preferable over direct data access, and this is even more so for global data.) By extension, the lack of access control greatly hinders achieving security in situations where you may wish to run untrusted code (such as working with 3rd party plugins).

Implicit coupling -- A program with many global variables often has tight couplings between some of those variables, and couplings between variables and functions. Grouping coupled items into cohesive units usually leads to better programs.

Concurrency issues -- if globals can be accessed by multiple threads of execution, synchronization is necessary (and too-often neglected). When dynamically linking modules with globals, the composed system might not be thread-safe even if the two independent modules tested in dozens of different contexts were safe.

Namespace pollution -- Global names are available everywhere. You may unknowingly end up using a global when you think you are using a local (by misspelling or forgetting to declare the local) or vice versa. Also, if you ever have to link together modules that have the same global variable names, if you are lucky, you will get linking errors. If you are unlucky, the linker will simply treat all uses of the same name as the same object.

Memory allocation issues -- Some environments have memory allocation schemes that make allocation of globals tricky. This is especially true in languages where "constructors" have side-effects other than allocation (because, in that case, you can express unsafe situations where two globals mutually depend on one another). Also, when dynamically linking modules, it can be unclear whether different libraries have their own instances of globals or whether the globals are shared.

Testing and Confinement - source that utilizes globals is somewhat more difficult to test because one cannot readily set up a 'clean' environment between runs. More generally, source that utilizes global services of any sort (e.g. reading and writing files or databases) that aren't explicitly provided to that source is difficult to test for the same reason. For communicating systems, the ability to test system invariants may require running more than one 'copy' of a system simultaneously, which is greatly hindered by any use of shared services - including global memory - that are not provided for sharing as part of the test.

Source: http://c2.com/cgi/wiki?GlobalVariablesAreBad

Sharing data in Angular

When it comes to sharing data across controllers in Angular you should use a service. With your custom service you can create a getter and a setter method. You inject it to the controllers you need it and can use it in your app.

There exist no performance issues. It would actually boost your performance by a fraction of time, because you dont need to dependency-inject a lot of services.

But it's a big concern of design. Consider a large application with dozens and dozens of views, complex components and tied to a number of well known APIs (e.g. Twitter, Flickr, Facebook, OAuth, ...).

You wont develop this application alone. The following issues will arise:

Namespacing

You are working on the Facebook API, someone else is working on the Twitter API. You both think that using $rootScope for functions is a good idea and both write a $rootScope.login function. How would you resolve this when doing git merge? You need namespacing, alas, you need to develop two services myFacebookAPI, myTwitterAPI which then can implement the same interface for loggin in. (login(user,pw)). Note that this gives you the ability to abstract away the actual social network you are dealing with in the controller, when you can do something like:

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

Testing

When developing professionally, you write tests. Angular gives you tools to do automated tests for services, etc., but you wont be able to test something you assign to $rootScope in the same comfortable manner.

Other issues will arise too, but i think this should be enough for you to think about it on your own.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!