login
欢迎访问QkqBeer博客园!

angularJS 学习记录(二)

首先,定义一个Angular模块(module)很简单,直接使用angular模块的module方法即可,如:

[javascript] view plain copy
 
  1. var myApp = angular.module('myApp', []);  

值得注意的是,module函数的第二个参数定义了该模块所依赖的模块。在首次定义一个模块时,必须同时指定模块名和依赖,尽管该模块可能没有依赖(使用空数组)。如果不指定依赖,则Angular会试图定位已经定义好的名称为myApp的模块,这通常会导致模块未定义错误。如果依赖不为空,则要保证其依赖的模块已经加载。这里面就有个加载顺序的问题。

最简单的情况下,我把使用模块所在的JS文件全部按顺序列在HTML的script元素中,只要保证模块之间的依赖关系正确即可,如:

 

[html] view plain copy
 
  1. <!doctype html>  
  2. <html lang="en" ng-app="myApp">  
  3. <head>  
  4.   <meta charset="utf-8">  
  5.   <title>My HTML File</title>  
  6.   <link rel="stylesheet" href="css/app.css">    
  7.   <script src="/scripts/myApp.js"></script>  
  8.   <script src="/scripts/app.js"></script>  
  9. </head>  
  10. <body>  
  11. <div>  
  12.     Hello Angular !  
  13. </div>  
  14. </body>  
  15. </html>  

 

如果app.js中的模块依赖模块myApp,则需先将定义它的myApp.js加载进来。在项目实践中,这种方法显然是不实际的,你不能在一开始把所有的JS文件全部加载进来。通常会使用一些延迟/异步加载机制,如使用RequireJS。

使用RequireJS定义的Angular模块可以是这样:

 

[javascript] view plain copy
 
  1. define(['angular'], function(angular) {  
  2.     angular.module('myApp', [])  
  3.       .controller('MyController', ['$scope', function ($scope) {  
  4.         //define scope data  
  5.       }]);  
  6. });  

其中define函数加载了angular.js,并取得angular模块,然后定义了myApp模块。如果myApp模块(或者MyController)依赖于其他模块(或组件),则需要告诉RequireJS在需要的时候加载:

 

 

[javascript] view plain copy
 
  1. define(['angular',   
  2.     './scripts/anotherApp',   
  3.     './scripts/service/utility'   
  4.     ], function(angular) {  
  5.         angular.module('myApp', ['newApp'])   
  6.             .controller('MyController', ['$scope', 'utils', function ($scope, utils) {   
  7.             //define scope data   
  8.         }]);   
  9. });  

这里,myApp依赖定义于anotherApp.js的newApp模块,并且需要定义于utility.js的工厂服务utils。这里存在的一个陷阱是,依赖的两个JS文件不一定会按照代码中声明的顺序加载,即utility.js可能先于anotherApp.js加载。因此在使用requireJS加载依赖时,要注意这些依赖本身之间的相互关系,不能指望requires按某种顺序加载这些依赖。否则,会导致一些概率性出现的问题,不易调试。

另外一点,如果在首次定义模块A时使用了空数组作为依赖,然后在某次使用A模块的过程中,误将依赖数组又传递一次,那么A模块原来的定义就会被覆盖,并且原来A模块定义的controller,service等组件也将不存在,这样也会导致难以调试的问题。因此在定义和使用Angular模块时不能大意。

-------------------------------------------------------------------------------------------------------------

2016/03/19 Update

谈到模块加载这个话题,就不能不提及Angular的启动(bootstrap)机制。简单来说,Angular的启动分为手动和自动两种。在前面的例子中,采用的就是自动的方式:通过内置的directive ngApp 来指定启动时加载的模块。

根据官方文档(这里),Angular的自动初始化发生在两个时机:

  1. 响应DOMContentLoaded事件(页面文档完全加载并解析完毕后会触发该事件,不会等待图片、样式文件)
  2. document.readyState的值为'complete'。(兼容IE8,页面完全加载,相当于load事件触发)

这时,Angular会调用angularInit方法进行初始化,首先查找ngApp directive,该directive指定了应用程序的根结点,通常位于初始页面的<html>标记上。当然,ngApp如果位于某个子结点(如div元素),则可以将Angular应用程序限制在DOM树的一部分上。如果Angular找到了ngApp,则会调用bootstrap方法开始启动过程,主要工作如下:

  1. 加载ngApp指定的模块
  2. 创建应用程序的依赖注入对象(injector)。此时$compile,$rootScope会被注入,用于后续的compile过程。有关依赖注入过程有后文。
  3. 以ngApp为根结点,compile整个DOM树。有关compile过程,在后续文章中详谈。

 

这个过程如下图所示。(图片来源:https://docs.angularjs.org/guide/bootstrap)。
 
有些情况下,开发者需要在初始化阶段做些额外的配置或控制,比如include一些模块或者需要在compile过程之前完成一些工作。这就需要手动启动。手动启动通过angular.bootstrap方法。一个例子如下:

 

[javascript] view plain copy
 
  1. <script>  
  2.     angular.module('myApp', [])  
  3.       .controller('MyController', ['$scope', function ($scope) {  
  4.       }]);  
  5.   
  6.     angular.element(document).ready(function() {  
  7.       angular.bootstrap(document, ['myApp']);  
  8.     });  
  9. </script>  

 

该方法的第一个参数指定了Angular应用所在的根DOM元素;第二个参数定义了需要依赖加载的模块(相当于ngApp),该模块必须事先定义完成(包括其controller,directive等组件),bootstrap方法不会定义该模块。(该方法还有第三个参数,参见这里)。一个Anuglar应用一旦启动,就不能再添加controller, service等组件。
有时,开发者不仅需要在启动阶段添加功能,还需要等待一些依赖项而延迟启动过程。这时可以使用angular.resumeBootstrap方法。如果window.name包含前缀NG_DEFER_BOOTSTRAP!,则当angular.bootstrap方法被调用时,启动过程会暂停,直到调用angular.resumeBootstrap方法。该方法会向启动需要的模块列表里加入额外的模块。例如:

 

[javascript] view plain copy
 
  1. window.name = "NG_DEFER_BOOTSTRAP!";  
  2. requirejs.config({  
  3.   baseUrl: '.',  
  4.   paths: {  
  5.     app: 'app/scripts/app',  
  6.     jquery: 'common/lib/jquery/dist/jquery',  
  7.     angular: 'common/lib/angular/angular'  
  8.   },  
  9.   shim: {  
  10.     angular : {   
  11.         exports : 'angular'  
  12.     }  
  13.   }  
  14. });  
  15.   
  16. require(['angular', 'jquery'], function(){  
  17.     require(['app'], function(app) {  
  18.       angular.element().ready(function() {  
  19.             angular.resumeBootstrap([app['name']]);  
  20.        });  
  21.     });  
  22. });  

有几点需要注意:

    1. 使用手动启动时,不要再指定ngApp directive。
    2. 在使用延迟启动时需要指定ngApp或者手动调用bootstrap方法,否则会报错:resumeBootstrap不是一个函数。原因是指定ngApp会内部调用bootstrap方法,而resumeBootstrap方法定义于bootstrap方法内部,如果没有指定ngApp,又没有手动调用bootstrap,则resumeBootstrap未定义。
    3. 如果在HTML指定了多个Angular实例(如多次加载angular.js),Angular只会启动第一个加载成功的实例启动。如果不延迟启动,则angular在遇到ng-app directive时开始初始化,并加载相应模块。如果需要加载额外的模块而需要延迟启动,则需定义window.name为NG_DEFER_BOOTSTRAP!,然后在所有模块加载完毕后,调用resumeBootstrap方法,继续启动,并加载指定模块。angular在自动加载时会调用bootstrap方法,如果没有指定ng-app,则需手动调用bootstrap方法。由于resumeBootstrap方法定义于bootstrap方法内部,因此如果bootstrap没有调用,就会报resumeBootstrap is not a function的错误。

转载自https://blog.csdn.net/jaytalent/article/details/50899677(大神讲的非常不错,适合稍微有些基础的人去学习)

posted @ 2018-06-25 17:18  BeerQkq  阅读(353)  评论(0编辑  收藏  举报