在本文中,我们将带你了解angularjs源码笔记(2)--loadermodules在这篇文章中,我们将为您详细介绍angularjs源码笔记(2)--loadermodules的方方面面,并解答a
在本文中,我们将带你了解angularjs源码笔记(2)--loader modules在这篇文章中,我们将为您详细介绍angularjs源码笔记(2)--loader modules的方方面面,并解答angular源码解析常见的疑惑,同时我们还将给您一些技巧,以帮助您实现更有效的Angularjs 2 模块(Modules)、angularjs angular-file-upload未知提供者:$uploadProvider错误、AngularJS Module 类的方法、AngularJs Modules详解及示例代码。
本文目录一览:- angularjs源码笔记(2)--loader modules(angular源码解析)
- Angularjs 2 模块(Modules)
- angularjs angular-file-upload未知提供者:$uploadProvider错误
- AngularJS Module 类的方法
- AngularJs Modules详解及示例代码
angularjs源码笔记(2)--loader modules(angular源码解析)
简介
ng提供模块管理,所有的service、ctrl等都是挂在模块上,没有依赖该模块那么这个模块的所有service、ctrl等都不能注入使用。
ng内置一个名为''ng''的模块,一些基础的服务如$http、$filter等都挂上面,默认所有的模块都依赖这个模块。
而实现那些依赖关系、加载先后顺序都是由loader这个模块完成。
源码分析
1. 全部源码
function setupModuleLoader(window) {
var $injectorMinErr = minErr(''$injector'');
var ngMinErr = minErr(''ng'');
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}
var angular = ensure(window, ''angular'', Object);
angular.$$minErr = angular.$$minErr || minErr;
return ensure(angular, ''module'', function() {
var modules = {};
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
if (name === ''hasOwnProperty'') {
throw ngMinErr(''badname'', ''hasOwnProperty is not a valid {0} name'', context);
}
};
assertNotHasOwnProperty(name, ''module'');
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
if (!requires) {
throw $injectorMinErr(''nomod'', "Module ''{0}'' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}
var invokeQueue = [];
var configBlocks = [];
var runBlocks = [];
var config = invokeLater(''$injector'', ''invoke'', ''push'', configBlocks);
var moduleInstance = {
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
requires: requires,
name: name,
provider: invokeLater(''$provide'', ''provider''),
factory: invokeLater(''$provide'', ''factory''),
service: invokeLater(''$provide'', ''service''),
value: invokeLater(''$provide'', ''value''),
constant: invokeLater(''$provide'', ''constant'', ''unshift''),
animation: invokeLater(''$animateProvider'', ''register''),
filter: invokeLater(''$filterProvider'', ''register''),
controller: invokeLater(''$controllerProvider'', ''register''),
directive: invokeLater(''$compileProvider'', ''directive''),
config: config,
run: function(block) {
runBlocks.push(block);
return this;
}
};
if (configFn) {
config(configFn);
}
return moduleInstance;
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {
queue[insertMethod || ''push'']([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
}
2. 分步解析
2.1 ensure使用
// 如果存在即返回,不存在使用factory创建,并保存
// 多次调用时保证不重复创建,返回同一个对象
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}
// 创建angular实例,为后续angular.module、angular.element等api调用准备
var angular = ensure(window, ''angular'', Object);
// 为angular实例创建module属性
ensure(angular, ''module'', function() {
// ...
});
2.2 module
function() {
if (!requires) {
throw $injectorMinErr(''nomod'', "Module ''{0}'' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}
var invokeQueue = [];
var configBlocks = [];
var runBlocks = [];
var config = invokeLater(''$injector'', ''invoke'', ''push'', configBlocks);
// 返回的实体
// 对外提供了如factory、service、value等api
// 然后都调用invokeLater这个方法
var moduleInstance = {
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
requires: requires,
name: name,
provider: invokeLater(''$provide'', ''provider''),
factory: invokeLater(''$provide'', ''factory''),
service: invokeLater(''$provide'', ''service''),
value: invokeLater(''$provide'', ''value''),
constant: invokeLater(''$provide'', ''constant'', ''unshift''),
animation: invokeLater(''$animateProvider'', ''register''),
filter: invokeLater(''$filterProvider'', ''register''),
controller: invokeLater(''$controllerProvider'', ''register''),
directive: invokeLater(''$compileProvider'', ''directive''),
config: config,
run: function(block) {
runBlocks.push(block);
return this;
}
};
if (configFn) {
config(configFn);
}
return moduleInstance;
}
还有值得一提的是,runBlocks和configFn明确地将过程分为config过程和run过程
2.3 invokeLater
// 延迟执行,现将执行的任务放入queue中,等待需要执行时再从queue中获取执行
// 调用invokeLater完成预注册的功能,等后续get特定module的时候再进行该module及依赖的module上的注册
function invokeLater(provider, method, insertMethod, queue) {
// 除config外其他所有都放入invokeQueue,config放入configBlocks
if (!queue) queue = invokeQueue;
return function() {
// 绝大多数是push,constant是unshift
// 因为constant是可以在config阶段注入到provider中,所以需要优先处理,故全部unshift到队列头
queue[insertMethod || ''push'']([provider, method, arguments]);
return moduleInstance;
};
}
2.4 bootstrap
ng通过bootstrap进行启动
angular.bootstrap(''#main'', [''mainModule'']);
通过bootstrap中加载配置的启动模块并根据注册module的依赖关系进行加载依赖module
// 将ng及$provide放入modules,顺序是 ng -> [$provide,fn] -> mainModule
modules = modules || [];
modules.unshift([''$provide'', function($provide) {
$provide.value(''$rootElement'', element);
}]);
modules.unshift(''ng'');
// 主要的加载依赖的逻辑在createInjector,在inject章节中分析
var injector = createInjector(modules, config.strictDi);
// 注入器(injector)执行页面的compile
injector.invoke([''$rootScope'', ''$rootElement'', ''$compile'', ''$injector'',
function(scope, element, compile, injector) {
scope.$apply(function() {
element.data(''$injector'', injector);
compile(element)(scope);
});
}]
);
2.5 loadModules
通过调用createInjector完成模块加载及处理,那么主要的加载逻辑就在createInjector中,选出其中相关代码 (ps: 本来这部分内容准备在inject中写,想了想还是移到这边)
function loadModules(modulesToLoad){
var runBlocks = [], moduleFn, invokeQueue;
forEach(modulesToLoad, function(module) {
// 判断是否已经加载过,防止重复加载
if (loadedModules.get(module)) return;
// 记录加载module
loadedModules.put(module, true);
function runInvokeQueue(queue) {
var i, ii;
for(i = 0, ii = queue.length; i < ii; i++) {
// invokeArgs结构例如 [$provider, ''factory'', arguments]
var invokeArgs = queue[i],
provider = providerInjector.get(invokeArgs[0]);
// 例如 $provide.factory.apply($provide, arguments);
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
}
try {
if (isString(module)) {
// 获取注册的module实例
moduleFn = angularModule(module);
// 递归调用loadModules, 将返回的runBlocks合并起来
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
// 执行之前需要延迟执行的方法
runInvokeQueue(moduleFn._invokeQueue);
runInvokeQueue(moduleFn._configBlocks);
}
// 如果是function或者array都作为runBlock处理
else if (isFunction(module)) {
runBlocks.push(providerInjector.invoke(module));
} else if (isArray(module)) {
runBlocks.push(providerInjector.invoke(module));
} else {
assertArgFn(module, ''module'');
}
} catch (e) {
if (isArray(module)) {
module = module[module.length - 1];
}
if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
// Safari & FF''s stack traces don''t contain error.message content
// unlike those of Chrome and IE
// So if stack doesn''t contain message, we create a new string that contains both.
// Since error.stack is read-only in Safari, I''m overriding e and not e.stack here.
/* jshint -W022 */
e = e.message + ''\n'' + e.stack;
}
throw $injectorMinErr(''modulerr'', "Failed to instantiate module {0} due to:\n{1}",
module, e.stack || e.message || e);
}
});
return runBlocks;
}
加载流程
- 执行 setupModuleLoader() ,准备好angular.module
- 用户调用 angular.module(moduleName, [], configFn),创建 module 并且声明依赖关系,返回module的实例,包含 requires 存放依赖,service\factory等方法
- 用户调用 module.factory 、module.provider 等创建服务,其实这时并没有真正创建,而是将创建的Fn和参数放入queue做了一个预注册连注册都不算,等待后续调用进行注册provider
- 用户调用 angular.bootstrap(''#main'', [''mainModule'']); 或者 ng-init 中声明启动的入口模块
- bootstrap方法中调用 createInjector,createInjector 中通过调用 loadModules 进行模块初始化及加载依赖模块,深度优先策略
- 先执行各种 provider 的注册,再执行 configFn,返回出收集齐的 runBlocks
- 这些 runBlocks 将在 createInjector 中执行,有个细节 runBlocks 收集时自己的 runBlocks 永远接在依赖模块的 runBlocks 后面,所以先执行依赖模块的,再执行自己的。
Angularjs 2 模块(Modules)
Angular 应用是模块化的,Angular 有自己的模块系统,叫做 Angular 模块 or NgModules。
.
每个 Angular 应用至少有一个模块——根模块,通常叫做 AppModule。
小规模的应用程序中也许只有一个根模块 , 大多数应用有许多功能模块,每个耦合的代码块作用于程序域、工作流或是密切相关的功能。
一个Angular模块,无论是根还是功能性,都是一个 使用@NgModule修饰符的类。
修饰符(decorators )是修改JavaScript类的功能函数。Angular有许多的修饰符,通过给类附加元数据可以知道这些类的的意义,它们如何工作。学习更多 关于网页元数据。
NgModule 是一个描述符函数,描述模块的单一元数据对象。最重要的属性是:
-
declarations - 属于这个模块的 视图类(view classes)。Angular 有三种视图类: components,directives,and pipes.
-
exports - 声明的一部分,对于其他模块的组件模板是可见和可用的。
-
imports - 声明这个模块的组件模版需要的、其他模块声明导出的类。
-
providers - 这个模块的服务创建器,是全局服务集合的一部分,可以被应用的任意部位访问到。
-
bootstrap - 主应用视图,叫做 根组件(root component),承载其他的应用视图。 只有 根模块(root module) 需要设置此引导属性。
这里是一个简单根模块:
import { NgModule } from '@angular/core'; import { browserModule } from '@angular/platform-browser'; @NgModule({ imports: [ browserModule ],providers: [ Logger ],declarations: [ AppComponent ],exports: [ AppComponent ],bootstrap: [ AppComponent ] }) export class AppModule { }
看 export 属性 AppComponent 就展示了如何export; 这里仅举例,并不实际需要。根模块没有理由export任何东西,因为其他组件不需要import根模块。
通过引导一个根模块启动一个应用。开发中你可以在main.ts中引导AppModule,如下所示:
import { platformbrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app.module'; platformbrowserDynamic().bootstrapModule(AppModule);
Angular 模块 对比 JavaScript 模块
The Angular module — 一个类修饰符使用@NgModule — 是 Angular 的基础功能。
JavaScript 同样也有自己的模块系统,管理Javascript对象集。这完全不同也和Angular 模块系统无关。
在 JavaScript 中, 每个 文件就是一个模块,所有定义在文件中的对象都属于模块。模块定义公有对象通过关键词export标记这些对象。其他的JavaScript模块使用 import statements来从其他模块访问这些共有对象。
import { NgModule } from '@angular/core'; import { AppComponent } from './app.component';
export class AppModule { }
在网上学习更过关于Javascript模块系统的知识。这是两种不同的和完整的模块系统,在应用中同时使用它们。
angularjs angular-file-upload未知提供者:$uploadProvider错误
我在视图中包含了所有必需的文件:
<script src="~/Scripts/angular-file-upload-master/examples/console-sham.min.js"></script> <script src="~/Content/js/angular.js"></script> <script src="~/Scripts/angular-file-upload-master/angular-file-upload.js"></script>
我的模块和控制器:
var controllers = angular.module('controllers',['ngGrid','ngDialog','angularFileUpload']); controllers.controller('CustomProductsCtrl',['$scope','$window','CommonService','CustomProductsServices','$upload',function ($scope,$window,ngDialog,CommonService,CustomProductsServices,$upload){ });
但我仍然得到这个错误.
错误:[$injector:unpr]未知提供者:$uploadProvider
请帮帮我.
解决方法
具体来说,你有:});当你应该有}]);代替.注意缺失].
在上下文中,您应该:
var controllers = angular.module('controllers',$upload){ }]); // Note: missing ']' added in here
因为我们需要遵循declaring a controller的形式.controller API很简洁,但非常简洁:
$controller(constructor,locals);
哪个扩展到您的情况:
module_name.controller( 'your_Ctrl',[locals,function(){ } ] );
我添加了额外的间距来调出缺失的]并显示我们如何关闭声明中的元素.
AngularJS Module 类的方法
AngularJS 中的 Module 类负责定义应用如何启动,它还可以通过声明的方式定义应用中的各个片段。我们来看看它是如何实现这些功能的。
一.Main 方法在哪里
如果你是从 Java 或者 Python 编程语言转过来的,那么你可能很想知道 AngularJS 里面的 main 方法在哪里?这个把所有东西启动起来,并且第一个被执行的方法在哪里?JavaScript 代码里面负责实例化并且把所有东西组合到一起,然后命令应用开始运行的那个方法在哪里?
事实上,AngularJS 并没有 main 方法,AngularJS 使用模块的概念来代替 main 方法。模块允许我们通过声明的方式来描述应用中的依赖关系,以及如何进行组装和启动。使用这种方式的原因如下:
1. 模块是声明式的。这就意味着它编写起来更加容易,同时理解起来也很容易,阅读它就像读普通的英文一样!
2. 它是模块化的。这就迫使你去思考如何定义你的组件和依赖关系,让它们变得更加清晰。
3. 它让测试更加容易。在单元测试吕,你可以有选择地加入模块,并且可以避免代码中存在无法进行单元测试的内容。同时,在场景测试中,你可以加载其他额外的模块,这样就可以更好地和其他组件配合使用。
例如,在我们的应用中有一个叫做 "MyAwesomeApp" 的模块。在 HTML 里面,只要把以下内容添加到 <html> 标签中(或者从技术上说,可以添加到任何标签中):
<html ng-app="MyAwesomeApp">
ng-app 指令就会告诉 AngularJS 使用 MyAwesomeApp 模块来启动你的应用。那么,应该如何定义模块呢?举例来说,我们建议你为服务、指令和过滤器分别定义不同的模块。然后你的主模块可以声明依赖这些模块。
这样可以使得模块管理更加容易,因为它们都是良好的、完备的代码块,每个模块有且只有一种职能。同时,单元测试可以只加载它们所关注的模块,这样就可以减少初始化的次数,单元测试也会变得更精致、更专注。
二。加载和依赖
模块加载动作发生在两个不同的阶段,这一点从函数名上面就可以反映出来,它们分别是 Config 代码块和 Run 代码块(或者叫做阶段)。
1.Config 代码块
在这一阶段里面,AngularJS 会连接并注册好所有数据源。因此,只有数据源和常量可以注入到 Config 代码块中。那些不确定是否已经初始化好的服务不能注入进来。
2.Run 代码块
Run 代码块用来启动你的应用,并且在注射器创建完成之后开始执行。为了避免在这一点开始之后再对系统进行配置操作,只有实例和常量可以被注入到 Run 代码块中。你会发现,在 AngularJS 中,Run 代码块是与 main 方法最类似的东西。
三。快捷方法
利用模块可以做什么呢?我们可以用它来实例化控制器、指令、过滤器以及服务,但是利用模块类还可以做更多事情。如下模块配置的 API 方法:
1.config(configFn)
利用此方法可以做一些注册工作,这些工作需要在模块加载时完成。
2.constant(name, object)
此方法会首先运行,所以你可以用它来声明整个应用范围内的常量,并且让它们在所有配置(config 方法)和实例(后面的所有方法,例如 controller、service 等)方法中可用。
3.controller(name,constructor)
它的基本作用是配置好控制器方便后面使用。
4.directive(name,directiveFactory)
可以使用此方法在应用中创建指令。
5.filter(name,filterFactory)
允许你创建命名的 AngularJS 过滤器,就像前面章节所讨论的那样。
6.run(initializationFn)
如果你想要在注射器启动之后执行某些操作,而这些操作需要在页面对用户可用之前执行,就可以使用此方法。
7.value(name,object)
允许在整个应用中注射值。
8.factory(name,factoryFn)
如果你有一个类或者对象,需要首先为它提供一些逻辑或者参数,然后才能对它初始化,那么你就可以使用这里的 factory 接口。factory 是一个函数,它负责创建一些特定的值(或者对象)。我们来看一个 greeter (打招呼) 函数的实例,这个函数需要一条问候语来初始化:
function Greeter(salutation) {
this.greet = function(name) {
return salutation + '' '' + name;
};
}
greeter 函数示例如下:
myApp.factory(''greeter'', function(salut) {
return new Greeter(salut);
});
然后可以这样来调用它:
var myGreeter = greeter(''Halo'');
9.service(name,object)
factory 和 service 之间的不同点在于,factory 会直接调用传递给它的函数,然后返回执行的结果;而 service 将会使用 "new" 关键字来调用传递给它的构造方法,然后再返回结果。所以,前面的 greeter Factory 可以替换成下面这个 greeter Service:
myApp.service(''greeter'', Greeter);
每当我们需要一个 greeter 实例的时候,AngularJS 就会调用新的 Greeter () 来返回一个实例。
10.provider(name,providerFn)
provider 是这几个方法中最复杂的部分(显然,也是可配置性最好的部分)。provider 中既绑定了 factory 也绑定了 service,并且在注入系统准备完毕之前,还可以享受到配置 provider 函数的好处(也就是 config 块)。
我们来看看使用 provider 改造之后的 greeter Service 是什么样子:
myApp.provider(''greeter'', function() {
var salutation = ''Hello'';
this.setSalutation = function(s) {
salutation = s;
}
function Greeter(a) {
this.greet = function() {
return salutation + '' '' + a;
}
}
this.$get = function(a) {
return new Greeter(a);
};
});
这样我们就可以在运行时动态设置问候语了(例如,可以根据用户使用的不同语言进行设置)。
var myApp = angular.module(myApp, []).config(function(greeterProvider) {
greeterProvider.setSalutation(''Namaste'');
});
每当有人需要一个 greeter 实例时,AngularJS 就会在内部调用 $get 方法。
附:angular.module (''MyApp'',[...]) 和 angular.module (''MyApp'') 之间有一个很小但是却很重要的不同点
angular.module (''MyApp'',[...]) 会创建一个新的 Angular 模块,然后把方括号([...])中的依赖列表加载进来;而 angular.module (''MyApp'') 会使用由第一个调用定义的现有的模块。
所以,对于以下代码,你需要保证在整个应用中只会使用一次:
angular.module(''MyApp'', [...]) //如果你的应用是模块化的,这里可能是MyModule
如果你不打算把模块的引用存到一个变量中,然后在整个应用中通过这个变量来引用模块,那么,在其他文件中使用 angular.module (MyApp) 的方式可以保证得到正确的 AngularJS 模块引用。模块上的所有东西都必须通过访问这个模块引用来定义,或者在模块定义的地方把那些必备的内容添加上去。
AngularJs Modules详解及示例代码
一、什么是Module?
很多应用都有一个用于初始化、加载(wires是这个意思吗?)和启动应用的main方法。angular应用不需要main方法,作为替代,module提供有指定目的的声明式,描述应用如何启动。这样做有几项优点:
- 这过程是声明描述的,更加容易读懂。
- 在单元测试中,不需要加载所有module,这对写单元测试很有帮助。
- 额外的module可以被加载到情景测试中,可以覆盖一些设置,帮助进行应用的端对端测试(end-to-end test)。
- 第三方代码可以作为可复用的module打包到angular中。
- module可以通过任意顺序或并行加载(取决于模块执行的延迟性,due to delayed nature of module execution)。
二、The Basics(基础)
我们很迫切想知道如何让Hello World module能够工作。下面有几个关键的事情要注意:
module API(http://code.angularjs.org/1.0.2/docs/api/angular.Module)
注意的提及的在<html ng-app=”myApp”>中的myApp module,它让启动器启动我们定义的myApp module。
<!DOCTYPE HTML> <html lang="zh-cn" ng-app="myApp"> <head> <meta charset="UTF-8"> <title>basics</title> <style type="text/css"> .ng-cloak { display: none; } </style> </head> <body> <div> {{''Kitty'' | greet}} </div> <script src="../angular-1.0.1.js" type="text/javascript"></script> <script type="text/javascript"> var simpleModule = angular.module("myApp", []); simpleModule.filter("greet", function () { return function(name) { return "Hello " + name + " !"; } }); </script> </body> </html>
三、(Recommended Setup)推荐设置
虽然上面的例子很简单,它不属于大规模的应用。我们建议将自己的应用按照如下建议,拆分为多个module:
- service module,用于声明service。
- directive module,用于声明directive。
- filter module,用于声明filter。
- 应用级别的module,依赖上述的module,并且包含初始化的代码。
这样划分的理由是,当我们在测试的时候,往往需要忽略那些让测试变得困难的初始化代码。通过将代码分成独立的module,在测试中就可以很容易地忽略那些代码。这样,我们就可以更加专注在加载相应的module进行测试。
上面的只是一个建议,可以随意地按照自己的需求制定。
四、Module Loading & Dependencies(模块加载和依赖)
module是配置(configuration)的集合,执行在启动应用的进程中应用的块(blocks)。在它的最简单的形式中,由两类block组成:
1.配置块(configuration blocks):在provider注册和配置的过程中执行的。只有provider和constant(常量?)可以被注入(injected)到configuration blocks中。这是为了避免出现在service配置完毕之前service就被执行的意外。
2.运行块(run blocks):在injector创建完成后执行,用于启动应用。只有实例(instances)和常量(constants)可以被注入到run block中。这是为了避免进一步的系统配置在程序运行的过程中执行。
angular.module(''myModule'', []). config(function(injectables) { // provider-injector // 这里是config block的一个例子 // 我们可以根据需要,弄N个这样的东东 // 我们可以在这里注入Providers (不是实例,not instances)到config block里面 }). run(function(injectables) { // instance-injector // 这里是一个run block的例子 // 我们可以根据需要,弄N个这样的东东 // 我们只能注入实例(instances )(不是Providers)到run block里面 });
a) Configuration Blocks(配置块)
有一个方便的方法在module中,它相当于config block。例如:
angular.module(''myModule'', []). value(''a'', 123). factory(''a'', function() { return 123; }). directive(''directiveName'', ...). filter(''filterName'', ...); // 等同于 angular.module(''myModule'', []). config(function($provide, $compileProvider, $filterProvider) { $provide.value(''a'', 123) $provide.factory(''a'', function() { return 123; }) $compileProvider.directive(''directiveName'', ...). $filterProvider.register(''filterName'', ...); });
configuration blocks被应用的顺序,与它们的注册的顺序一致。对于常量定义来说,是一种额外的情况,即放置在configuration blocks开头的常量定义。
b) Run Blocks(应用块)
run block是在angular中最接近main方法的东东。run block是必须执行,用于启动应用的代码。它将会在所有service配置、injector创建完毕后执行。run block通常包含那些比较难以进行单元测试的代码,就是因为这个原因,这些代码应该定义在一个独立的module中,让这些代码可以在单元测试中被忽略。
c) Dependencies(依赖)
一个module可以列出它所依赖的其他module。依赖一个module,意味着被请求(required)的module(被依赖的)必须在进行请求(requiring)module(需要依赖其他module的module,请求方)加载之前加载完成。换一种说法,被请求的module的configuration block会在请求的module的configuration block执行前执行(before the configuration blocks or the requiring module,这里的or怎么解释呢?)。对于run block也是这个道理。每一个module只能够被加载一次,即使有多个其他module需要(require)它。
d) Asynchronous Loading(异步加载)
module是管理$injector配置的方法之一,不用对加载脚本到VM做任何事情。现在已经有现成的项目专门用于处理脚本加载,也可以用到angular中。因为module在加载的过程中不做任何事情,它们可以按照任意的顺序被加载到VM中。脚本加载器可以利用这个特性,进行并行加载。
五、Unit Testing(单元测试)
在单元测试的最简单的形式中,其中一个是在测试中实例化一个应用的子集,然后运行它们。重要的是,我们需要意识到对于每一个injector,每一个module只会被加载一次。通常一个应用只会有一个injector。但在测试中,每一个测试用例都有它的injector,这意味着在每一个VM中,module会被多次加载。正确地构建module,将对单元测试有帮助,正如下面的例子:
在这个例子中,我们准备假设定义如下的module:
angular.module(''greetMod'', []). factory(''alert'', function($window) { return function(text) { $window.alert(text); }; }) .value(''salutation'', ''Hello'') .factory(''greet'', function(alert, salutation) { return function(name) { alert(salutation + '' '' + name + ''!''); }; });
让我们写一些测试用例:
describe(''myApp'', function() { // 加载应用响应的module,然后加载指定的将$window重写为mock版本的测试module, // 这样做,当进行window.alert()时,测试器就不会因被真正的alert窗口阻挡而停止 //这里是一个在测试中覆盖配置信息的例子 beforeEach(module(''greetMod'', function($provide) {//这里看来是要将真正的$window替换为以下的东东 $provide.value(''$window'', { alert: jasmine.createSpy(''alert'') }); })); // inject()会创建一个injector,并且注入greet和$window到测试中。 // 测试不需要关心如何写应用,只需要关注如何测试应用。 it(''should alert on $window'', inject(function(greet, $window) { greet(''World''); expect($window.alert).toHaveBeenCalledWith(''Hello World!''); })); // 这里是在测试中通过行内module和inject方法来覆盖配置的方法 it(''should alert using the alert service'', function() { var alertSpy = jasmine.createSpy(''alert''); module(function($provide) { $provide.value(''alert'', alertSpy); }); inject(function(greet) { greet(''World''); expect(alertSpy).toHaveBeenCalledWith(''Hello World!''); }); }); });
- Angular 理解module和injector,即依赖注入
- 深入浅析AngularJS中的module(模块)
- 详解AngularJS中module模块的导入导出
- AngularJS Module方法详解
- Angular ng-repeat 对象和数组遍历实例
- 基于AngularJS实现iOS8自带的计算器
- 微信+angularJS的SPA应用中用router进行页面跳转,jssdk校验失败问题解决
- AngularJS 实现JavaScript 动画效果详解
- 深入理解AngularJS中的ng-bind-html指令和$sce服务
- Angular Module声明和获取重载实例代码
我们今天的关于angularjs源码笔记(2)--loader modules和angular源码解析的分享已经告一段落,感谢您的关注,如果您想了解更多关于Angularjs 2 模块(Modules)、angularjs angular-file-upload未知提供者:$uploadProvider错误、AngularJS Module 类的方法、AngularJs Modules详解及示例代码的相关信息,请在本站查询。
本文标签: