TAT.mandyluo angular 应用如何实现按需加载
In 未分类 on 2015年10月13日 by view: 7,951
13

KindEditor

我们有个系统是用 angular 开发的,是一个单页面应用,随着系统的迭代,首屏代码已经过于庞大,所以对系统进行改造。
我们主要面临 3 个问题
1. 是否需要模块加载框架?
2. 异步加载回来的页面组件,如何注册?
3. 在什么时机加载页面组件?

针对第一个问题,由于 angular 自身已经有一套模块化方案,再引入模块加载框架有点冗余,而且整体改造量比较大,所以不考虑。
因此只是实现了一个 loadscript 方法,用来加载组件。稍微需要注意的是加载多个文件时候的串并行,和避免页面重复切换时的重复加载。
第二个问题比较蛋疼,angular 有 “启动” 的说法,“启动” 发生在 domcontentloaded 之后,会把所有注入到主模块中的依赖编译一遍。
QQ图片20151013120836.png
启动之后再想使用 controller、deractive 等 api,会直接报错
QQ图片20151013144848.png
目前来看,解决这个问题,只有一个方法,就是利用主模块的 provider 主动注册 controller,但是由于 provider 不能直接使用,所以我们把它存在主模块下面。
QQ图片20151012202840.png
通过存下来的方法,可以用来注册异步加载回来的页面组件。缺点就是这样子所有子页面都挂在主模块下了。
针对第三个问题,由于运营平台是单页面应用,最好的加载时机应该是路由监听到哈希变化时,但是由于我们的路由是写死的静态配置,一开始没找到什么好的办法。
后来发现了这样一个 api
QQ图片20151013150020.png
大概是说,在 $routeChangeSuccess 之前,我们还可以做些东西,把加载时机放在这里最适合不过啦
具体实现大概是这样子
QQ图片20151013150414.png
至此,这个方案已经通了,剩下什么工作?
1. 整理代码,使代码更通用化,我们以后开发新页面,只需要在路由配置里这样写就可以啦
QQ图片20151013150806.png
2. 把现有页面都改造一下,由于之前没有按需加载,不同页面之间的 service 耦合严重,今后我们开发新页面,就要注意不同页面之间共用的 service 最好放在 component 下面
3. 改构建,给路由里的 js 引用换成 cdn 路径。

原创文章转载请注明:

转载自AlloyTeam:http://www.alloyteam.com/2015/10/angular-application-how-to-load-on-demand/

  1. ZABAKAWIS 2016 年 9 月 28 日

    版本号怎么解决呢?

  2. 愛你多一點 2015 年 10 月 29 日

    不知道 LZ 具体怎么实现的,有一个问题请教,首先我的主模块已经做了类似:
    app.controller = $controllerProvider.register; 等这一系列定义,
    接着在定义路由时,我的 resolve 这样定义,homeCtrl.js 文件加载进来了,但是 homeCtrl 这个控制器还是没有注册到主模块上,导致依旧提示如下错误:
    Error: [ng:areq] Argument ‘homeCtrl’ is not a function, got undefined
    $stateProvider.state(“home”, {
    url: “/home”,
    templateUrl: “js/views/home/index.html”,
    controller: “homeCtrl”,
    resolve: [‘$ocLazyLoad’,’$q’, function($ocLazyLoad, $q){
    var deferred = $q.defer();
    $ocLazyLoad.load({
    name: “OursLove”,
    files: [“js/controllers/homeCtrl.js”]
    })
    .finally(function(){
    deferred.resolve();
    });
    return deferred.promise;
    }]
    });
    homeCtrl.js 的定义:
    define([“app”], function (app) {
    //也就是程序加载了 homeCtrl.js 这个文件,但是未执行进这个函数
    return app.controller(“homeCtrl”, [“$scope”, “$rootScope”, “$http”, function ($scope, $rootScope, $http) {
    $rootScope.headTitle = “HOME”;
    }]);
    });

    • 愛你多一點 2015 年 10 月 29 日

      很用心的给代码做了缩进,可是为什么提交之后缩进没了呢 [疑问]

    • 愛你多一點 2015 年 10 月 29 日

      var app = angular.module(“OursLove”, [“ui.router”, “oc.lazyLoad”]);

    • 孙跃 2015 年 11 月 11 日

      你用了 ui-router 和 ocLazyLoad 的话,只需要在 resolve 那里直接 return $ocLazyLoad(); 就行了,返回的本来就是个 promise

  3. 杨焱 2015 年 10 月 14 日

    还是我,之前我也是第一次用 angular 开发项目,也是想着能够按需加载,但是失败了。看了你的文章后也试着实现了下按需加载。但总觉得方法不是最好,能否分享下 bpController 这个方法吗?

    • TAT.mandyluo 2015 年 10 月 14 日

      bpController 只是一个 controller,不同的是使用了之前提到的保存下来的 app.controller

      • 杨焱 2015 年 10 月 15 日

        是,我就是想了解下 angular 里怎么通过一个 controller function 来渲染一个模板的,我的做法是 controllerFunction.apply(this, controllerFunction.providers), 但是碰到 $scope,$rootScope 和 $location 这些就有问题了,只好先在我的 bpController 里申明这几个 provider, 再传到 controllerFunction 里,明显这种方法有问题。

    • TAT.mandyluo 2015 年 10 月 14 日

      有更好的方法讨论一下吗?

  4. yy 2015 年 10 月 13 日

    图片挂了,

发表评论到 TAT.mandyluo