AngularJs中的延迟加载

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

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

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的例子:

// 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

(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

angular.module('app').controllerProvider.resgister('SomeLazyController', function($scope)
{
    $scope.key = '...';
});
        但问题依然存在,我们在什么地方延时加载这些controller文件,来取代使用<script>。目前,仅仅有一个地方可以完成这个工作,那就是在定义路由属性的地方。
当使用$routeProvider服务来定义路由时,你可以指定一个key/factory的依赖(页面与js文件的依赖)map,把映射注入到路由的controller中。这个map名为'resolve':
$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加载器来进行依赖的延迟加载:

$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的通知:
$rootScope.$apply(function()
{
    deferred.resolve();
});
        如果不在$rootScope中的$apply方法中执行promise,route将不会在初始化page时触发。
        现在应用以上的内容来定义app module,如下:

appModule.js

(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

// 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'关联起来。

原文链接: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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值