在编写大型JavaScript应用程序时,最简单的操作之一就是将代码库分成几个文件。 这样做可以提高代码的可维护性,但增加了在主HTML文档中丢失或放错脚本标签的机会。 随着文件数量的增加,跟踪依赖项变得很困难。 这个问题在大型AngularJS应用程序中也仍然存在。 我们有许多工具可用来在应用程序中加载依赖项。
在本文中,我们将看到如何在AngularJS中使用RequireJS来简化加载依赖项的工作。 我们还将研究如何使用Grunt生成包含RequireJS模块的组合文件。
RequireJS简介
RequireJS是一个JavaScript库,可帮助延迟加载JavaScript依赖项。 模块只是其中包含一些RequireJS语法糖的JavaScript文件。 RequireJS实现CommonJS指定的异步模块 。 RequireJS提供了简单的API来创建和引用模块。
RequireJS需要一个主文件,其中包含基本配置数据,例如模块和垫片的路径。 以下代码片段显示了main.js
文件的框架:
require.config({
map:{
// Maps
},
paths:{
// Aliases and paths of modules
},
shim:{
// Modules and their dependent modules
}
});
无需在路径部分中指定应用程序中的所有模块。 可以使用它们的相对路径加载其他文件。 要定义模块,我们需要使用define()
块。
define([
// Dependencies
], function(
// Dependency objects
){
function myModule() {
// Can use the dependency objects received above
}
return myModule;
});
一个模块可能具有一些从属模块。 通常,对象是在模块末尾返回的,但这不是强制性的。
Angular的依赖注入与RequireJS依赖管理
我从Angular开发人员那里听到的常见问题之一是Angular的依赖管理与RequireJS之间的区别。 重要的是要记住,这两个库的目的完全不同。 AngularJS内置的依赖注入系统处理组件中所需的对象; 而RequireJS中的依赖项管理则处理模块或JavaScript文件。
当RequireJS尝试加载模块时,它将检查所有相关模块并首先加载它们。 缓存已加载模块的对象,并在再次请求相同模块时为它们提供服务。 另一方面,AngularJS维护带有名称和对应对象列表的注入器。 创建组件时,会将条目添加到进样器,并且只要使用注册名称引用该对象,就可以使用该对象。
一起使用RequireJS和AngularJS
本文附带的可下载代码是一个包含两个页面的简单应用程序。 它具有以下外部依赖性:
- 需求JS
- jQuery的
- AngularJS
- 角线
- 角资源
- Angular UI ngGrid
这些文件应按照此处列出的顺序直接加载到页面上。 我们有五个自定义脚本文件,其中包含所需AngularJS组件的代码。 让我们看看如何定义这些文件。
将AngularJS组件定义为RequireJS模块
任何AngularJS组件都包含:
- 函数定义
- 依赖注入
- 注册到Angular模块
在上述三个任务中,我们将在各个模块中执行前两个任务,而第三个任务将在一个单独的模块中执行,该模块负责创建AngularJS模块。
首先,让我们定义一个配置块。 config块不依赖于其他任何块,最后返回config函数。 但是,在将配置模块加载到另一个模块中之前,我们需要加载配置块所需的所有内容。 以下代码包含在config.js
:
define([],function(){
function config($routeProvider) {
$routeProvider.when('/home', {templateUrl: 'templates/home.html', controller: 'ideasHomeController'})
.when('/details/:id',{templateUrl:'templates/ideaDetails.html', controller:'ideaDetailsController'})
.otherwise({redirectTo: '/home'});
}
config.$inject=['$routeProvider'];
return config;
});
请注意,以上代码段中执行依赖项注入的方式。 我使用$inject
获取$inject
的依赖项,因为上面定义的config函数是普通的JavaScript函数。 在关闭模块之前,我们返回config函数,以便可以将其发送到相关模块以供进一步使用。
我们也使用相同的方法来定义任何其他类型的Angular组件,因为这些文件中没有任何组件特定的代码。 以下代码段显示了控制器的定义:
define([], function() {
function ideasHomeController($scope, ideasDataSvc) {
$scope.ideaName = 'Todo List';
$scope.gridOptions = {
data: 'ideas',
columnDefs: [
{field: 'name', displayName: 'Name'},
{field: 'technologies', displayName: 'Technologies'},
{field: 'platform', displayName: 'Platforms'},
{field: 'status', displayName: 'Status'},
{field: 'devsNeeded', displayName: 'Vacancies'},
{field: 'id', displayName: 'View Details', cellTemplate: '<a ng-href="#/details/{{row.getProperty(col.field)}}">View Details</a>'}
],
enableColumnResize: true
};
ideasDataSvc.allIdeas().then(function(result){
$scope.ideas=result;
});
}
ideasHomeController.$inject=['$scope','ideasDataSvc'];
return ideasHomeController;
});
应用程序的Angular模块取决于到目前为止定义的每个模块。 该文件从所有其他文件中获取对象,并将其与AngularJS模块挂钩。 该文件可能会或可能不会返回任何结果,因此可以使用angular.module()
从任何地方引用Angular模块。 以下代码块定义了一个Angular模块:
define(['app/config',
'app/ideasDataSvc',
'app/ideasHomeController',
'app/ideaDetailsController'],
function(config, ideasDataSvc, ideasHomeController, ideaDetailsController){
var app = angular.module('ideasApp', ['ngRoute','ngResource','ngGrid']);
app.config(config);
app.factory('ideasDataSvc',ideasDataSvc);
app.controller('ideasHomeController', ideasHomeController);
app.controller('ideaDetailsController',ideaDetailsController);
});
无法使用ng-app
指令引导Angular应用ng-app
因为所需的脚本文件是异步加载的。 正确的方法是使用手动引导。 这必须在名为main.js
的特殊文件中main.js
。 这需要首先定义Angular模块的文件。 该文件的代码如下所示。
require(['app/ideasModule'],
function() {
angular.bootstrap(document, ['ideasApp']);
}
);
配置Grunt以合并RequireJS模块
在部署JavaScript繁重的应用程序时,应将脚本文件合并并缩小以优化脚本文件的下载速度。 诸如Grunt之类的工具可自动完成这些任务。 它定义了许多任务,以简化任何前端部署过程。 它具有grunt-contrib-requirejs任务,用于以正确的顺序组合RequireJS文件模块,然后最小化生成的文件。 与其他繁重的任务一样,可以将其配置为在部署的每个阶段表现不同。 可以在演示应用程序中使用以下配置:
requirejs: {
options: {
paths: {
'appFiles': './app'
},
removeCombined: true,
out: './app/requirejs/appIdeas-combined.js',
optimize: 'none',
name: 'main'
},
dev:{
options:{
optimize:'none'
}
},
release:{
options:{
optimize:'uglify'
}
}
}
当使用dev选项运行Grunt时,此配置将生成一个未压缩的文件,而当使用release选项运行grunt时,此配置将生成一个最小的文件。
结论
当应用程序的大小超出一定数量的文件时,管理依赖项就变得很困难。 像RequireJS这样的库使定义依赖项变得更加容易,而不必担心文件的加载顺序。 依赖管理正在成为JavaScript应用程序的组成部分。 AngularJS 2.0将内置对AMD的支持。
From: https://www.sitepoint.com/using-requirejs-angularjs-applications/