打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
AngularJs中的延迟加载
        当你使用AngularJs中的routes/views模式建立大型网站或者应用的时候,把所有的自定义文件,如controllers和directives等在初始化时全部加载进来,确实不是一个好的办法。最好的方式是,初始化时仅仅加载所需要的文件。这些文件可能会依赖一个连接或者多个文件,然而它们仅仅被特定的route所使用。当我们切换route时,未被加载的文件将会按需加载。这不但能提高初始化页面的速度,而且可以防止带宽浪费。这篇文章,我将展示如何进行延迟加载。
下面有两个问题:
1.当application启动完成之后,针对一个module如何进行延迟文件的加载?
2.在application中,代替你选择的script加载器,应该在哪里进行实际的加载?

        问题1造成的原因是,在application启动以后,使用module API无法进行文件的注册。也就是说,如果你想在启动后的app中创建一个新的controller,如下:

[javascript] view plaincopy
  1. angular.module('app').controller('SomeLazyController'function($scope)  
  2. {  
  3.     $scope.key = '...';  
  4. });  
        那么当你使用ng-controller标签关联这个controller时,会产生下面的异常:
  1. Error: Argument ‘SomeLazyController’ is not a function, got undefined  
        目前,据我所了解,在启动后的application中注册文件,唯一的方法不是使用module API,而是使用AngularJs的第三方服务。

        第三方服务是由一些对象组成,这些对象用来创建和配置AngularJs文件的实例。因此,我们用$controllerProvider服务来进行controller的延迟注册。以此类推,$compileProvider服务用来延迟注册directive,$filterProvider服务用来延迟注册filter,$provider服务用来延迟注册service。下面是关于controller和service的例子:

[javascript] view plaincopy
  1. // Registering a controller after app bootstrap  
  2. $controllerProvider.register('SomeLazyController'function($scope)  
  3. {  
  4.    $scope.key = '...';   
  5. });  
  6.    
  7. // Registering a directive after app bootstrap  
  8. $compileProvider.directive('SomeLazyDirective'function()  
  9. {  
  10.     return {  
  11.         restrict: 'A',  
  12.         templateUrl: 'templates/some-lazy-directive.html'  
  13.     }  
  14. })  
  15.    
  16. // etc  

        第三方服务仅仅在module配置期间生效。因此,他们之间一直保持着联系,用来延迟注册文件。例如,通过保持一个相关的服务,你可以像下面的例子那样建立app module:

appModule.js

[javascript] view plaincopy
  1. (function()  
  2. {  
  3.     var app = angular.module('app', []);  
  4.    
  5.     app.config(function($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide)  
  6.     {  
  7.         app.controllerProvider = $controllerProvider;  
  8.         app.compileProvider    = $compileProvider;  
  9.         app.routeProvider      = $routeProvider;  
  10.         app.filterProvider     = $filterProvider;  
  11.         app.provide            = $provide;  
  12.    
  13.         // Register routes with the $routeProvider  
  14.     });  
  15. })();  
        然后就可以用下面的方法延迟注册controller:

someLazyController.js

[javascript] view plaincopy
  1. angular.module('app').controllerProvider.resgister('SomeLazyController'function($scope)  
  2. {  
  3.     $scope.key = '...';  
  4. });  
        但问题依然存在,我们在什么地方延时加载这些controller文件,来取代使用<script>。目前,仅仅有一个地方可以完成这个工作,那就是在定义路由属性的地方。
当使用$routeProvider服务来定义路由时,你可以指定一个key/factory的依赖(页面与js文件的依赖)map,把映射注入到路由的controller中。这个map名为'resolve':
[javascript] view plaincopy
  1. $routeProvider.when('/about', {templateUrl:'views/about.html', controller:'AboutViewController' resolve:{key:factory});  

        map中的'key'表示依赖的名称,而'factory'可以是一个已存在的service的别名(string),该service将作为依赖使用,也可以是一个可注入的方法(function),方法的返回值将作为依赖使用。如果这个方法返回了一个promise,该promise会在route触发之前运行。因此,这些依赖会进行异步的重新获取,就像延迟加载文件那样,我们使用依赖map中的方法会得到一个promise,一旦文件被延迟加载了,这个promise就会运行。这将确保在route被触发之前,所有的文件都会被延迟加载。下面是一个路由定义的例子,其中指定了使用$script.js加载器来进行依赖的延迟加载:

[javascript] view plaincopy
  1. $routeProvider.when('/about', {templateUrl:'views/about.html', resolve:{deps:function($q, $rootScope)  
  2. {  
  3.     var deferred = $q.defer();  
  4.     var dependencies =  
  5.     [  
  6.         'controllers/AboutViewController.js',  
  7.         'directives/some-directive.js'  
  8.     ];  
  9.    
  10.     // Load the dependencies  
  11.     $script(dependencies, function()  
  12.     {  
  13.         // all dependencies have now been loaded by so resolve the promise  
  14.         $rootScope.$apply(function()  
  15.         {  
  16.             deferred.resolve();  
  17.         });  
  18.     });  
  19.    
  20.     return deferred.promise;  
  21. }}});  
        需要注意的是,在上面的例子中,promise的运行过程在$scriptjs的上下文之中,却不在AngularJs的上下文之中,所以在promise运行时要通知AngularJs。在$rootScope中的$apply方法中执行promise,可以实现对AngularJs的通知:
[javascript] view plaincopy
  1. $rootScope.$apply(function()  
  2. {  
  3.     deferred.resolve();  
  4. });  
        如果不在$rootScope中的$apply方法中执行promise,route将不会在初始化page时触发。
        现在应用以上的内容来定义app module,如下:

appModule.js

[javascript] view plaincopy
  1. (function()  
  2. {  
  3.     var app = angular.module('app', []);  
  4.    
  5.     app.config(function($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide)  
  6.     {  
  7.         app.controllerProvider = $controllerProvider;  
  8.         app.compileProvider    = $compileProvider;  
  9.         app.routeProvider      = $routeProvider;  
  10.         app.filterProvider     = $filterProvider;  
  11.         app.provide            = $provide;  
  12.    
  13.         // Register routes with the $routeProvider  
  14.         $routeProvider.when('/', {templateUrl:'views/home.html'});  
  15.         $routeProvider.when('/about', {templateUrl:'views/about.html', resolve:{deps:function($q, $rootScope)  
  16.         {  
  17.             var deferred = $q.defer();  
  18.             var dependencies =  
  19.             [  
  20.                 'controllers/AboutViewController.js',  
  21.                 'directives/some-directive.js'  
  22.             ];  
  23.    
  24.             $script(dependencies, function()  
  25.             {  
  26.                 // all dependencies have now been loaded by $script.js so resolve the promise  
  27.                 $rootScope.$apply(function()  
  28.                 {  
  29.                     deferred.resolve();  
  30.                 });  
  31.             });  
  32.    
  33.             return deferred.promise;  
  34.         }}});  
  35.     });  
  36. })();  
最后,你可以使用$script.js相同的方式启动app:

appBootStrap.js

[javascript] view plaincopy
  1. // This file will be loaded from index.html  
  2. $script(['appModule.js'], function()  
  3. {  
  4.     angular.bootstrap(document, ['app'])  
  5. });  

        这些就是在AngularJs中实现延迟加载的大概步骤了。总的来说,首先你要确定app module持有相关providers的实例。然后确保使用的是这些providers来延迟注册你所需的文件,而不要使用module API。接着在你的路由定义中,通过'resolve'方法返回一个promise,一旦路由触发,便加载延迟的文件并运行promise。这确保在路由触发之前,你的所有延迟文件都会被加载完成。千万不要忘记当作用域在AngularJs上下文之外时,在$rootScope的$apply方法中执行promise。再后,在启动app之前,你要创建'bootstrap'文本来首次加载app module。最后,将'bootstrap'文本与你的'index.html'关联起来。

原文链接:http://ify.io/lazy-loading-in-angularjs/

参考文章:

http://www.bennadel.com/blog/2553-loading-angularjs-components-after-your-application-has-been-bootstrapped.htm

http://www.bennadel.com/blog/2554-loading-angularjs-components-with-requirejs-after-application-bootstrap.htm




本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
AngularJS路由和模板
Angular项目构建指南 - 不再为AngularJS构建而犹豫不决
AngularJS入门教程07:路由与多视图 | AngularJS中文社区
AngularJs路由:ngRoute
AngularJS进阶(三十六)AngularJS项目开发技巧之利用Service&Promise&Resolve解决图片预加载问题(后记)
angularJS 显示带html的文本
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服