首页 > 网站 > WEB开发 > 正文

【原创】angularjs1.3.0源码解析之执行流程

2024-04-27 14:19:11
字体:
来源:转载
供稿:网友

【原创】angularjs1.3.0源码解析之执行流程

Angular执行流程

前言

发现最近angularjs在我厂的应用变得很广泛,下周刚好也有个angular项目要着手开始做,所以先做了下功课,从源代码开始入手会更深刻点,可能讲的没那么细,侧重点在于整个执行流程,之后会对angularjs的各个模块再解析下。

目录结构

angularFiles.js中就可以看到angular.js是如何打包的?

  'angularSrc': [    'src/minErr.js',    'src/Angular.js',    'src/loader.js',    'src/AngularPublic.js',    'src/jqLite.js',    'src/apis.js',    'src/auto/injector.js',    'src/ng/anchorScroll.js',    'src/ng/animate.js',    'src/ng/asyncCallback.js',    // ...省略若干service    'src/ng/filter.js',    'src/ng/filter/filter.js',    'src/ng/filter/filters.js',    // ...省略若干filter    'src/ng/directive/directives.js',    'src/ng/directive/a.js',    'src/ng/directive/attrs.js',    // ...省略若干directive  ]

核心代码(angular.js)

这里精简了代码,省略了angular很多的方法定义,直接从执行流程入手,看angular如何进行一个程序的初始化。

(function(window, document, undefined) {  // ...省略若干代码    // 判断代码angularjs重复加载  if (window.angular.bootstrap) {       console.log('WARNING: Tried to load angular more than once.');    return;  }  // 绑定jQuery或者jqLite,实现angular.element    bindJQuery();  // 暴露api,挂载一些通用方法,如:angular.forEach  // 实现angular.module,并定义模块ng,以及ngLocale  publishExternalAPI(angular);  // 当dom ready时,开始执行程序的初始化  jqLite(document).ready(function() {  // 初始化入口    angularInit(document, bootstrap);  });})(window, document);

publishExternalAPI函数(src/AngularPublic.js)

  1. 将一些通用方法挂载到全局变量angular上
  2. 注册两个模块ngngLocale(其中ng依赖ngLocale)
  3. ng模块的回调函数用来注册angular内置的servicedirective(该回调将在angularInit后被执行)
function publishExternalAPI(angular){  // 将通用方法挂载到全局变量augular上  extend(angular, {    'bootstrap': bootstrap,    'copy': copy,    'extend': extend,    'equals': equals,    'element': jqLite,    'forEach': forEach,    'injector': createInjector,    // ...省略若干通用方法  });  // 实现angular.module方法并赋值给angularModule  angularModule = setupModuleLoader(window);  try {    // 获取ngLocale模块    // 如果获取不到,则会报出异常    angularModule('ngLocale');  } catch (e) {    // 接受异常(也就是没有获取不到ngLocale模块)    // 在这里注册ngLocale模块    angularModule('ngLocale', []).PRovider('$locale', $LocaleProvider);  }  // 注册ng模块(此模块依赖ngLocale模块)  // 回调中注册N多service,以及N多directive(回调等待初始化angularInit后执行)  angularModule('ng', ['ngLocale'], ['$provide',    function ngModule($provide) {      // $$sanitizeUriprovider needs to be before $compileProvider as it is used by it.      $provide.provider({        $$sanitizeUri: $$SanitizeUriProvider      });      $provide.provider('$compile', $CompileProvider).        directive({            a: htmlAnchorDirective,            input: inputDirective,            textarea: inputDirective,            form: formDirective,            // ...省略若干directive        }).        directive({          ngInclude: ngIncludeFillContentDirective        }).        directive(ngAttributeAliasDirectives).        directive(ngEventDirectives);      $provide.provider({        $anchorScroll: $AnchorScrollProvider,        $animate: $AnimateProvider,        $browser: $BrowserProvider,        // ...省略若干service      });    }  ]);}

setupModuleLoader函数(src/loader.js)

这里定义了angular.module方法,该方法用来注册模块并返回模块实例,像上面所说的ngngLocale模块都是通过它注册的。

function setupModuleLoader(window) {  // 异常处理,可以忽略  var $injectorMinErr = minErr('$injector');  var ngMinErr = minErr('ng');  // 获取指定obj的name属性  // 不存在的话,利用factory函数创建并存储在obj.name下,方便下次获取  function ensure(obj, name, factory) {    return obj[name] || (obj[name] = factory());  }  // 获取angular全局变量  var angular = ensure(window, 'angular', Object);  angular.$$minErr = angular.$$minErr || minErr;  // 定义angular.module方法并返回  return ensure(angular, 'module', function() {    // 利用闭包,缓存模块实例    var modules = {};    // angular.module 的方法实现    // 如果参数是一个,获取指定name的模块(getter操作)    // 否则,(重新)创建模块实例并存储(setter操作),最后返回    return function module(name, requires, configFn) {          // 检测模块名不能是'hasOwnProperty'      var assertNotHasOwnProperty = function(name, context) {        if (name === 'hasOwnProperty') {          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);        }      };            // 检测模块名      assertNotHasOwnProperty(name, 'module');            // 如果参数不止一个(requires存在,哪怕是[]),表现为setter操作      // 如果该模块已存在,那么置为null,重新创建      if (requires && modules.hasOwnProperty(name)) {        modules[name] = null;      }            // 获取指定name模块的模块      // 从modules缓存中取或者(重新)创建新的模块实例      return ensure(modules, name, function() {              // 程序走到这里,表示是新模块的创建过程        // 而requires如果为空,则表示是获取已有的模块        // 两者其实是相互矛盾的,所以抛出异常,说明该模块还没有注册        // 所以我们在创建模块时,就算没有依赖其他模块,写法也应该是:        // angular.module('myModule', []);        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);        // 将要返回的module实例        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;        // 通过该方法对module实例的一系列常用方法进行包装,如myModule.provider,myModule.controller        // 我们在调用myModule.provider(...)时实质上是数据存储(push或者unshift)而不是立即注册服务        // 这一点我们从invokeLater的字面意思(之后再调用)也可以看出        // 那么真正的执行(如注册服务),是在angularInit之后,准确的说是在loadModules的时候(之后会说到)        function invokeLater(provider, method, insertMethod, queue) {                  // 默认队列是invokeQueue,也可以是configBlocks          // 默认队列操作是push,也可以是unshift          if (!queue) queue = invokeQueue;          return function() {            queue[insertMethod || 'push']([provider, method, arguments]);            return moduleInstance;          };        }      });    };  });}

angularInit函数(src/Angular.js)

当dom ready时,该函数开始运行,通过调用bootstrap函数进行整个angular应用的初始化工作。这里传递给bootstrap两个函数:应用根节点(含有xx-app属性的dom)和启动模块(xx-app的值)xx 为 ['ng-', 'data-ng-', 'ng:', 'x-ng-'] 任一一种,这里做了兼容多种属性名

function angularInit(element, bootstrap) {  var appElement,      module,      config = {};  // ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', &
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表