AngularJs中的延迟加载

AngularJs中的延迟加载

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

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

1
2
3
angular.module( 'app' ).controller( 'SomeLazyController' , function ($scope) {
     $scope.key = '...' ;
});

那么当你使用ng-controller标签关联这个controller时,会产生下面的异常:

Error: Argument SomeLazyController is not a function, got undefined

目前,据我所了解,在启动后的application中注册文件,唯一的方法不是使用module API,而是使用AngularJs的第三方服务。

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

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

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

appModule.js

1
2
3
4
5
6
7
8
9
10
11
12
13
( function () {
     var app = angular.module( 'app' , []);
  
     app.config( function ($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
         app.controllerProvider = $controllerProvider;
         app.compileProvider    = $compileProvider;
         app.routeProvider      = $routeProvider;
         app.filterProvider     = $filterProvider;
         app.provide            = $provide;
  
         // Register routes with the $routeProvider
     });
})();

然后就可以用下面的方法延迟注册controller:

someLazyController.js

1
2
3
angular.module( 'app' ).controllerProvider.resgister( 'SomeLazyController' , function ($scope) {
     $scope.key = '...' ;
});

但问题依然存在,我们在什么地方延时加载这些controller文件,来取代使用<script>。目前,仅仅有一个地方可以完成这个工作,那就是在定义路由属性的地方。
当使用$routeProvider服务来定义路由时,你可以指定一个key/factory的依赖(页面与js文件的依赖)map,把映射注入到路由的controller中。这个map名为’resolve’:

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加载器来进行依赖的延迟加载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$routeProvider.when( '/about' , {templateUrl: 'views/about.html' , resolve:{deps: function ($q, $rootScope) {
     var deferred = $q.defer();
     var dependencies =
     [
         'controllers/AboutViewController.js' ,
         'directives/some-directive.js'
     ];
  
     // Load the dependencies
     $script(dependencies, function () {
         // all dependencies have now been loaded by so resolve the promise
         $rootScope.$apply( function () {
             deferred.resolve();
         });
     });
  
     return deferred.promise;
}}});

需要注意的是,在上面的例子中,promise的运行过程在$scriptjs的上下文之中,却不在AngularJs的上下文之中,所以在promise运行时要通知AngularJs。在$rootScope中的$apply方法中执行promise,可以实现对AngularJs的通知:

1
2
3
$rootScope.$apply( function () {
     deferred.resolve();
});

如果不在$rootScope中的$apply方法中执行promise,route将不会在初始化page时触发。
现在应用以上的内容来定义app module,如下:

appModule.js

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

最后,你可以使用$script.js相同的方式启动app:

appBootStrap.js

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

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值