AngularJS能够很好地与RequireJS(http://www.requirejs.org/)配合使用,这使得我们可以同时拥有两种组件的优点。下面通过配置AngularJS官方的AngularSeed实例,来看一下AngularJS与RequireJS集成组织项目的方式。
项目结构:
首先,我们需要main.js文件,RequireJS将会加载这份文件,然后这份文件将会触发加工其他所有依赖的东西。
main.js
/** * 定义RequireJS配置 */ require.config({ paths: { 'angular': '../lib/angular/angular', 'angular-route': '../lib/angular-route/angular-route', 'domReady': '../lib/requirejs-domready/domReady' }, shim: { 'angular': { exports: 'angular' }, 'angular-route': { deps: ['angular'] } }, deps: [ // kick start application... see bootstrap.js './bootstrap' ] }); require( [ 'app', //注意:这不是Twitter Bootstrap,而是AngularJS bootstrap 'bootstrap', //所创建的所有控制器、服务、指令及过滤器文件都必须写到这里,这块内容必须手动维护 'controllers/controllers', 'services/services', 'directives/directives', 'filters/filters' ], function(app) { 'use strict'; return app.config( [ '$routeProvider', function($routeProvider) { $routeProvider.when('/view1', { templateUrl : 'partials/partial1.html', controller : 'MyCtrl1' }); $routeProvider.when('/view2', { templateUrl : 'partials/partial2.html', controller : 'MyCtrl2' }); $routeProvider.otherwise( { redirectTo : '/view1' }); } ]); } );
然后定义一个app.js文件。这个文件定义了AngularJS应用,并且告诉它去依赖我们所定义的所有控制器、服务、过滤器及指令。
你可以把RequireJS依赖列表看作JavaScript版的阻塞型import语句。也就是说,除非依赖列表中的内容都已经加载完或准备好,否则语句块中的函数不会开始执行。
同时要注意,我们不会单独让RequireJS去加载指令、服务或过滤器,因为这不是当前这个项目的组织方式。对于每个控制器、服务、过滤器及指令,都有一个模块与之对应,因此,只要把这些模块定义成我们所依赖的东西就够了。
app.js
define([ 'angular', 'angular-route', './controllers/index', './directives/index', './filters/index', './services/index' ], function (angular) { 'use strict'; return angular.module('app', [ 'controllers', 'directives', 'filters', 'services', 'ngRoute' ]); });
我们还有一份bootstrap.js文件,它将会等待DOM结构准备好(使用RequireJS的插件domReady),然后告诉AngularJS一切都已就绪,可以开始运行了。
bootstrap.js
//当DOM结构加载完成后,bootstrap.js文件将会命令AngularJS启动起来并继续执行 define(['angular', 'domReady', 'app'], function(angular, domReady) { require(['domReady!'], function (document) { angular.bootstrap(document, ['app']); }); });
把启动过程和应用代码分离开还有另一个好处,那就是,为了测试,我们可能会潜在地把mainApp替换成虚拟的代码或者mockApp。例如,如果所依赖的服务器是不可靠的,你可能会创建一个fackApp,用来代替所有$http请求,它会返回一些虚拟的数据来保证开发过程顺利进行。这样一来,你可能只是把fakeBootstrap和fackApp放到了应用中。
index.html
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>AngularJS与RequireJS集成App</title> <link rel="stylesheet" href="css/app.css"> </head> <body> <ul class="menu"> <li><a href="#/view1">view1</a></li> <li><a href="#/view2">view2</a></li> </ul> <div data-ng-view></div> <div>Angular Require seed app: v<span app-version></span></div> <script src="lib/requirejs/require.js" data-main="js/main.js"></script> </body> </html>
下面来看看js/controllers/controllers.js文件里面的内容,它看起来几乎和js/directives/directives.js、js/filters/filters.js及js/services/services一模一样。
define(['angular'], function (angular) { 'use strict'; return angular.module('controllers', []); });
我们组织RequireJS依赖关系的方式会保证在Angular所依赖的内容加载并准备好之后,所有的东西才会运行。
js/controllers/、js/directives/、js/filters/及js/services/里面都会定义一个AngularJS模块,并且对于每一个单独的控制器、指令、过滤器及服务,在声明的时候都会添加对应的模块依赖。
js/controllers/index.js
define([ './my-ctrl-1', './my-ctrl-2' ], function () {});
js/directives/index.js
define(['./app-version'], function () {});
再进一步看一下控制器和指令的定义:
my-ctrl-1.js
define(['controllers/controllers'], function (controllers) { 'use strict'; controllers.controller('MyCtrl1', [function ($scope) {}]); });
my-ctrl-2.js
define(['controllers/controllers'], function (controllers) { 'use strict'; controllers.controller('MyCtrl2', [function ($scope) {}]); });
app-version.js
define(['directives/directives'], function (directives) { 'use strict'; directives.directive('appVersion', ['version', function (version) { return function (scope, elm) { elm.text(version); }; }]); });
指令本身是非常琐碎的,但是我们还是来仔细看一下正在发生什么事情。RequireJS将会在文件两端添加一些东西,这些东西会说我的app-version.js文件会依赖模块声明文件directives/directives.js。然后这份文件就会使用插入了指令的模块去添加自已的指令声明。你可以把单条或者多条指令放在同一份文件里面,这完全由你自已决定。
其中有一个主要的注意点:如果你有一个控制器,且这个控制器会从一个后台服务中拉取数据(例如RootConroller依赖于UserService,它被注入了UserService),这种情况下,你就必须确保在RequireJS中也定义了服务文件的依赖,示例如下:
define(['controllers/controllers', 'services/services'], function(controllers) { controllers.controller('RootController', ['$scope', 'UserService', function($scope, UserService) { //做需要做的事情 }; }]); });
以上就是整个源码文件夹基本的配置方式。
参考书籍:《用AngularJS开发下一代Web应用》
参考源码:https://github.com/StarterSquad/startersquad.com/tree/master/examples/angularjs-requirejs-1
附件工程源码:
1.原来的AngularJSSeed工程源码AngularJSSeed.rar
2.与RequireJS集成后的工程源码AngularRequireSeed.rar
3.github上的工程源码AngularRequireJSSeed.rar