angularjs学习笔记——使用requirejs动态注入控制器

最近一段时间在学习angularjs,由于觉得直接使用它需要加载很多的js文件,因此想使用requirejs来实现异步加载,并动态注入控制器。简单搜索了下发现好多教程写的都很复杂,所以打算写一下我的方法,算是学习笔记了。

demo目录如下图:
demo目录结构

index.html文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo</title>
<!-- 引入requirejs,并在main.js中初始化 -->
<script data-main="main.js" src="libs/require.js">
</script>
</head>
<body>
<div ng-view></div>
</body>
</html>

在引入main.js后,就需要在其中完成requirejs的初始化:

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
33
// main.js
'use strict';
(function (win) {
require.config({
baseUrl: './',
// 依赖相对路径
paths: {
'angular': 'libs/angular.min',
'angular-route': 'libs/angular-route.min'
},
// 引入没有使用requirejs模块写法的类库
shim: {
'angular': {
exports: 'angular'
},
'angular-route': {
// angular-route依赖angular
deps: ['angular'],
exports: 'ngRoute'
}
}
});
// 自动导入router.js模块,由于后缀名可以省略,故写作'router',
// 并将模块返回的结果赋予到router中。
require(['angular','router'], function(angular,router){
// 手动启动angularjs,特别说明此处的bootstrap不是那个ui框架,
// 而是angularjs的一个手动启动框架的函数
angular.bootstrap(document, ['blogApp']);
});
})(window);

main.js中完成了各模块的初始化,并且引入了router.js
下面我们在router.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
33
34
35
36
37
38
39
40
41
42
43
44
// router.js
define(['angular', 'require', 'angular-route'], function (angular, require) {
var blogApp = angular.module('blogApp',['ngRoute']);
blogApp.config(['$routeProvider','$controllerProvider',
function($routeProvider,$controllerProvider) {
$routeProvider
.when('/', {
templateUrl:'templates/list.html',
controller: 'ListCtrl',
resolve:{
delay : ctrlRegister('ListCtrl',['controllers/ListCtrl.js'])
}
})
.when('/data', {
templateUrl:'templates/data.html',
controller: 'DataCtrl',
resolve:{
delay : ctrlRegister('DataCtrl',['controllers/DataCtrl.js'])
}
})
.otherwise({
redirectTo: '/'
});
function ctrlRegister (ctrlName, ctrlModule) {
return function ($q) {
var defer = $q.defer();
require(ctrlModule, function (controller) {
$controllerProvider.register(ctrlName, controller);
defer.resolve();
});
return defer.promise;
}
}
}
]);
return blogApp;
});

我把这里面拆为分三个部分来说
第一部分:定义该模块

1
2
3
4
5
6
7
8
9
10
11
12
// 引入3个基础模块
define(['angular', 'require', 'angular-route'], function (angular, require) {
// 定义整个demo为一个名为blogApp的模块
var blogApp = angular.module('blogApp',['ngRoute']);
// ...第二部分:路由配置...
// ...第三部分:复用的动态注入控制器函数
// 向main.js返回这个blogApp
return blogApp;
});

第二部分:设置基础路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
blogApp.config(['$routeProvider','$controllerProvider',
function($routeProvider,$controllerProvider) {
$routeProvider
.when('/', {
// 模板地址
templateUrl:'templates/list.html',
// 控制器的名字
controller: 'ListCtrl',
// resolve用来在完成路由前处理一些事
// 这里用来动态加载并注入相应的控制器
resolve:{
// ctrlRegister为我自己写的一个复用的函数,
// 用于注入控制器。见第三部分
delay : ctrlRegister('ListCtrl',['controllers/ListCtrl.js'])
}
});
}
}

第三部分:复用的控制器注入函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 该函数接受两个参数
// ctrlName,字符串类型,为该控制器的名字
// ctrlModule,字符串数组类型,为要引入的控制器的相对地址
// 调用例如 ctrlRegister('ListCtrl',['controllers/ListCtrl.js'])
function ctrlRegister (ctrlName, ctrlModule) {
return function ($q) {
var defer = $q.defer();
// 加载该控制器,并将返回值赋给controller,返回值一般是一个控制器函数
require(ctrlModule, function (controller) {
// 将返回值注册为名称为ctrlName的控制器
$controllerProvider.register(ctrlName, controller);
defer.resolve();
});
// 完成注册
return defer.promise;
}
}

好了,这样就完成了动态加载的功能了,下面就可以写要动态加载的控制器了
用其中一个控制器ListCtrl.js来说明问题:

1
2
3
4
5
6
7
8
9
// ListCtrl.js
// 加载angular模块
define(['angular'], function (angular) {
将本控制器函数作为结果返回给router.js
return function ListCtrl( $scope ){
$scope.lists = ['1','2','3'];
};
});

剩下的事情就是在list.html中接收控制器传送的数据了:

1
2
3
4
<!-- list.html -->
<ul>
<li ng-repeat="list in lists"><a href="#/{{list}}" ng-bind="list"></a></li>
</ul>

最终实现的功能是:
比如我访问http://127.0.0.1/b06-blog/#/只会加载list.htmlListCtrl.js
而当访问http://127.0.0.1/b06-blog/#/data就只会加载data.htmlDataCtrl.js
这样做有什么好处呢?当有很多控制器时,可以按需加载相应的控制器,不会一股脑全部加载上来(看起来依然并没有什么卵用)。
很惭愧,只为大家节约了一点微小的带宽,谢谢大家。