GVKun编程网logo

angularjs源码笔记(2)--loader modules(angular源码解析)

18

在本文中,我们将带你了解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)--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;
}

加载流程

  1. 执行 setupModuleLoader() ,准备好angular.module
  2. 用户调用 angular.module(moduleName, [], configFn),创建 module 并且声明依赖关系,返回module的实例,包含 requires 存放依赖,service\factory等方法
  3. 用户调用 module.factory 、module.provider 等创建服务,其实这时并没有真正创建,而是将创建的Fn和参数放入queue做了一个预注册连注册都不算,等待后续调用进行注册provider
  4. 用户调用 angular.bootstrap(''#main'', [''mainModule'']);  或者 ng-init 中声明启动的入口模块
  5. bootstrap方法中调用 createInjector,createInjector 中通过调用 loadModules 进行模块初始化及加载依赖模块,深度优先策略
  6. 先执行各种 provider 的注册,再执行 configFn,返回出收集齐的 runBlocks
  7. 这些 runBlocks 将在 createInjector 中执行,有个细节 runBlocks 收集时自己的 runBlocks 永远接在依赖模块的 runBlocks 后面,所以先执行依赖模块的,再执行自己的。

 

Angularjs 2 模块(Modules)

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错误

angularjs angular-file-upload未知提供者:$uploadProvider错误

这不是 This Question的重复

我在视图中包含了所有必需的文件:

<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 类的方法

 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详解及示例代码

AngularJs Modules详解及示例代码

一、什么是Module?

  很多应用都有一个用于初始化、加载(wires是这个意思吗?)和启动应用的main方法。angular应用不需要main方法,作为替代,module提供有指定目的的声明式,描述应用如何启动。这样做有几项优点:

  1. 这过程是声明描述的,更加容易读懂。
  2. 在单元测试中,不需要加载所有module,这对写单元测试很有帮助。
  3. 额外的module可以被加载到情景测试中,可以覆盖一些设置,帮助进行应用的端对端测试(end-to-end test)。
  4. 第三方代码可以作为可复用的module打包到angular中。
  5. module可以通过任意顺序或并行加载(取决于模块执行的延迟性,due to delayed nature of module execution)。
  6.  

二、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:

  1. service module,用于声明service。
  2. directive module,用于声明directive。
  3. filter module,用于声明filter。
  4. 应用级别的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 modulesangular源码解析的分享已经告一段落,感谢您的关注,如果您想了解更多关于Angularjs 2 模块(Modules)、angularjs angular-file-upload未知提供者:$uploadProvider错误、AngularJS Module 类的方法、AngularJs Modules详解及示例代码的相关信息,请在本站查询。

本文标签: