angular使用requirejs/ui-router/angularAMD实现动态加载模块

来源:互联网 发布:南方电网待遇 知乎 编辑:程序博客网 时间:2024/06/05 18:52

最近在做angular项目,发现使用requirejs来加载模块的时候,它会根据已经建立好的依赖一次性把所有模块加载下来,而我们的项目中有两个不同的产品,我们希望进入每个产品时只加载该产品对应的模块。但是要结合ui-router来实现,网上的例子还实在少,好不容易有同事搜到类似的解决方案,试了半天终于成功了,原文章在这里:

http://christopherthielen.github.io/ui-router-extras/#/future


文章写的相当简洁,具体概念原理大家自己去看文章吧,这里就大家分享一下本项目中如何使用它的。。

1,首先需要引用一些库:

bower install angularAMD --save       ---------angularAMD用来动态加载js文件和js模块,依赖于angular。这里我们使用angularAMD中的ngload来动态加载模块

bower install ui-router-extra --save    --------- ui-router-extra是ui-router的扩展包,主要是用到其中的futureState。futureState是在config时候定义,但真正跳转的时候会用一个full ui-router替换。这个full ui-router到时候会用$stateProvider。具体在例子里面解释。


2. 在require的config文件里面定义各种引用:

require.config({
    baseUrl: '../', 
    paths: {

        ...
       'angularUiRouterExtra':'xxx/ui-router-extras/release/ct-ui-router-extras.min', //for lazy load
        'angularAMD':'xxx/angularAMD/angularAMD.min', //for lazy load
        'ngload':'xxx/angularAMD/ngload.min', //for lazy load
    },

    shim: {
        ...
        'angularAMD':{
            deps:['angular']
        },
        'angularUiRouter': {
            deps: ['angular']
        },

        'ngload':{
            deps:['angularAMD']
        },
        'angularUiRouterExtra':{
            deps:['angular']
        }

        ...
    }


});
    require(['app'], function(app) { //init the application
        app.init();
    });


3. 定义futureState的声明

定义一个futureStates.json文件:

[{
    "stateName": "module1", //future state name
    "urlPrefix": "/module1",     //url
    "type": "ngload",
    "src": "xxx/module1.js"
  }...]

用一个json文件是为了便于管理到时候要lazy load的那些state


4. module1.js  -----this is the module to lazy load

define(['angularAMD',
  ], function(angularAMD) {
  'use strict';
   var module1 = angular.module('module1',['ui.router']);
   var mainState = {                             //this is the full ui-router  state definition
      name:'module1',
      url:'/module1',
      templateUrl:'xxx/test.html',
      controller:function($scope){
        $scope.test = "test";
      }
   };

   module1.config(['$stateProvider', function ($stateProvider) {
    $stateProvider.state(rfxState);   //use $stateProvider to register full ui-router  state definition
    }]);


   return  { mainState: mainState, module: module1 };  //这里一定要这样返回啊!跟以前直接返回一个module不一样哦!
});


5. app.js

define([
  'angularAMD',                 //for lazy loading,replace angular with angularAMD
  'angularUiRouter',           //define
  'angularUiRouterExtra',  //for lazy loading

   ...
], function(angularAMD, _) {
  'use strict';
  var app = angular.module('app', [
    'ui.router',
    'ct.ui.router.extras',

    ...
  ]);

  app.init = function() {
    angularAMD.bootstrap(app);     //replace angular.bootstrap(document, ['app']);
  };
  app.config(['$stateProvider', '$urlRouterProvider','$controllerProvider','$futureStateProvider',
    function($stateProvider, $urlRouterProvider,$controllerProvider, $futureStateProvider) {
      var loadAndRegisterFutureStates = function($http) {
        return $http.get('xxx/futureStates.json').then(function (resp) { 
          angular.forEach(resp.data, function (fstate) {
            // Register each state returned from $http.get() with $futureStateProvider
            $futureStateProvider.futureState(fstate);     //#1

   // futureStates.json contains all the future states declaration

          });
        });
      };

//#2
      $futureStateProvider.stateFactory('ngload', ngloadStateFactory);// register AngularAMD ngload state factory

//#3
      $futureStateProvider.addResolve(loadAndRegisterFutureStates);   
      
    }
  ]);


  app.run(['$rootScope',
    function($rootScope) {
      $rootScope._ = _;
    }
  ]);

  return app;

  function ngloadStateFactory($q, futureState) {
    var ngloadDeferred = $q.defer();
    require([ "ngload!" + futureState.src , 'ngload', 'angularAMD'],
        function ngloadCallback(result, ngload, angularAMD) {
          angularAMD.processQueue();
          ngloadDeferred.resolve(result.entryState);
        });
    return ngloadDeferred.promise;

 
});


好了代码上完了,下面解释一下这些代码的工作流程和原理。

在以前的做法中,我们直接使用requirejs来建立app module对module1的依赖关系:

define([
  ...

  ’xxx/module1'          //include module1.js file before loading app.js

   ...
], function(angular, _) {
  'use strict';
  var app = angular.module('app', [
   ...
    'module1',          //build the dependency of module1,

    ...]);            

}

这样,在加载app module之前,根据requirejs建立的依赖关系,angular会先去加载module1模块


而现在,我们不在定义中建立这种依赖关系,当我们启动app模块时,module1模块还没有被加载进来。这时候如果我们通过ui访问module1 state时(比如点击module1的state或者href),但其实这个module1 state还不存在(还没加载进来),然后$futureStatesProvider会去futureState中看有没有module1 state的定义。(#1的code,futureStates.json里面的futureState定义,$futureStateProvider.futureState(fstate);)。当找到这个futureState(里面定义了type为ngload, 并且有url和src文件位置)时,ngloadStateFactory就起作用了。

#2的code,$futureStateProvider.stateFactory('ngload', ngloadStateFactory); 注册了type为ngload(动态加载模块)的factory, 该factory将会用build full Ui-Router module1。ngloadStateFactory的写法大家看代码,这里就不啰嗦了。

最后看#3的代码$futureStateProvider.addResolve (resolveFunction)。这个方法其实就是添加了一个resolve函数,这个函数返回的是个promise。具体概念自己去查,这里就不介绍了。。


总结:

发现把代码扒拉干净以后其实好简单,但里面有些概念和写法还是半知半解的。另外,原文里面的例子还提供了iframeStateFactory和lazyCtrlStateFactory,这里项目没有用到,也就没有去试,大家有兴趣可以自己去实现看看,实现以后记得共享一下哦




0 0