angular源码分析:完整加载流程
一、从源代码的编译顺序开始下面是我们在目录结构哪一期理出的angular的编辑顺序图的缩略版: ├─── angular.prefix //util.wrap函数加入的前缀代码 │ ├─── minErr.js //错误处理 ├─── Angular.js //主要定义angular的工具函数 ├─── loader.js //定义了setupModuleLoader函数 ├─── stringify.js //定义了对象序列化serializeObject,和对象调试输出字符串serializeObject ├─── AngularPublic.js //定义了angular导出的函数和变量 ├─── jqLite.js //定义jqLite,一个mini的jQuery ├─── apis.js //定义了关于对象hash值的几个函数 ├─── auto │ │ │ └─── injector.js //依赖注入和模块加载,主要在这里实现,我在[第二期]讲过部分 │ ├─── ng //定义angular的各种服务的目录,该目录下一个文件按名字对应一个服务 │ │ │ ├─── *.js //各种服务的定义 │ ├─── filter.js //定义过滤器,注册具体的过滤器 │ ├─── filter //过滤器目录, │ │ │ │ │ └─── *.js //过滤器的具体实现 │ └─── directive //指令目录,该目录下一个文件对应一个angular指令 │ │ │ └─── *.js //指令的具体实现 ├─── angular.bind.js //简单几行代码,判断是否已经加载了jQuery,如果是,直接使用jQuery,而不使用jqLite ├─── publishExternalApis.js // `publishExternalAPI(angular);`导出变量和接口 │ └── angular.suffix //util.wrap函数加入的后缀代码 二、找到代码的入口点//try to bind to jquery now so that one can write angular.element().read() //but we will rebind on bootstrap again. bindJQuery(); //绑定jquery:如果系统已经加载了jQuery,绑定使用,如果没有则是用angular自身的jqLite publishExternalAPI(angular); //导出angular的对外公开的函数和属性 jqLite(document).ready(function() { //等待dom加载完后启动angular angularInit(document,bootstrap); }); 三、dom加载前的准备工作1.bindJQuery这里将bindJQuery的代码贴出来,看看 function bindJQuery() { // bind to jQuery if present; jQuery = window.jQuery; // reset to jQuery or default to us. if (jQuery) { //如果使用jQuery,对其做了些扩展处理 jqLite = jQuery; extend(jQuery.fn,{ scope: JQLitePrototype.scope,isolateScope: JQLitePrototype.isolateScope,controller: JQLitePrototype.controller,injector: JQLitePrototype.injector,inheritedData: JQLitePrototype.inheritedData }); //下面是对jquery改变dom时,增加一些清理工作,包括remove(删除元素),empty(置空),html(重写元素内容时) // Method signature: // jqLitePatchJQueryRemove(name,dispatchThis,filterElems,getterIfNoArguments) jqLitePatchJQueryRemove('remove',true,false); jqLitePatchJQueryRemove('empty',false,false); jqLitePatchJQueryRemove('html',true); } else { jqLite = JQLite; } angular.element = jqLite; } 2.publishExternalAPI下面把publishExternalAPI的代码也贴出来 function publishExternalAPI(angular){ extend(angular,{ //导出工具函数,就是直接将要导出的函数和属性加到angular对象上。细心的同学会发现下面少了angular.module的module。 'bootstrap': bootstrap,'copy': copy,'extend': extend,'equals': equals,'element': jqLite,'forEach': forEach,'injector': createInjector,'noop':noop,'bind':bind,'toJson': toJson,'fromJson': fromJson,'identity':identity,'isUndefined': isUndefined,'isDefined': isDefined,'isString': isString,'isFunction': isFunction,'isObject': isObject,'isNumber': isNumber,'isElement': isElement,'isArray': isArray,'version': version,'isDate': isDate,'lowercase': lowercase,'uppercase': uppercase,'callbacks': {counter: 0},'$$minErr': minErr,'$$csp': csp }); angularModule = setupModuleLoader(window); //这里需要重点分析 try { angularModule('ngLocale'); } catch (e) { angularModule('ngLocale',[]) .provider('$locale',$LocaleProvider); } angularModule('ng',['ngLocale'],['$provide',//定义“ng”模块,$provide作为后面函数的依赖注入对象,可见$provide是在之前的模块中定义的 function ngModule($provide) { // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. $provide.provider({ //注册$$sanitizeUri服务 $$sanitizeUri: $$SanitizeUriProvider }); $provide .provider('$compile',$CompileProvider) //定义$compile服务 .directive({ //注册各种指令 a: htmlAnchorDirective,input: inputDirective,textarea: inputDirective,form: formDirective,script: scriptDirective,select: selectDirective,style: styleDirective,option: optionDirective,ngBind: ngBindDirective,ngBindHtml: ngBindHtmlDirective,ngBindTemplate: ngBindTemplateDirective,ngClass: ngClassDirective,ngClassEven: ngClassEvenDirective,ngClassOdd: ngClassOddDirective,ngCloak: ngCloakDirective,ngController: ngControllerDirective,ngForm: ngFormDirective,ngHide: ngHideDirective,ngIf: ngIfDirective,ngInclude: ngIncludeDirective,ngInit: ngInitDirective,ngNonBindable: ngNonBindableDirective,ngPluralize: ngPluralizeDirective,ngRepeat: ngRepeatDirective,ngShow: ngShowDirective,ngStyle: ngStyleDirective,ngSwitch: ngSwitchDirective,ngSwitchWhen: ngSwitchWhenDirective,ngSwitchDefault: ngSwitchDefaultDirective,ngOptions: ngOptionsDirective,ngTransclude: ngTranscludeDirective,ngModel: ngModelDirective,ngList: ngListDirective,ngChange: ngChangeDirective,required: requiredDirective,ngRequired: requiredDirective,ngValue: ngValueDirective }) .directive({ //注册ngInclude 指令 ngInclude: ngIncludeFillContentDirective }) .directive(ngAttributeAliasDirectives) //? .directive(ngEventDirectives);//? $provide.provider({ //注册剩余的各种服务 $anchorScroll: $AnchorScrollProvider,$animate: $AnimateProvider,$browser: $BrowserProvider,$cacheFactory: $CacheFactoryProvider,$controller: $ControllerProvider,$document: $DocumentProvider,$exceptionHandler: $ExceptionHandlerProvider,$filter: $FilterProvider,$interpolate: $InterpolateProvider,$interval: $IntervalProvider,$http: $HttpProvider,$httpBackend: $HttpBackendProvider,$location: $LocationProvider,$log: $LogProvider,$parse: $ParseProvider,$rootScope: $RootScopeProvider,$q: $QProvider,$sce: $SceProvider,$sceDelegate: $SceDelegateProvider,$sniffer: $SnifferProvider,$templateCache: $TemplateCacheProvider,$timeout: $TimeoutProvider,$window: $WindowProvider }); } ]); } 细心的同学会发现:在导出的工具函数数组中少了angular.module的module,先不管,往下看。 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) {...}; }); } ensure(obj,factory)函数的含义:如果obj对象上存在name属性,直接返回;如果不存在,通过factory进行构造。 下面是 function module(name,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,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 runBlocks = []; var config = invokeLater('$injector','invoke'); var moduleInstance = { // Private state _invokeQueue: invokeQueue,_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',controller: invokeLater('$controllerProvider',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) { return function() { invokeQueue[insertMethod || 'push']([provider,arguments]); return moduleInstance; }; } }); } 分析invokeLater函数:这个函数的功能是返回一个函数,这个函数能向invokeQueue数组中插入一个三元组(provider,arguments) 四、dom加载后的工作:
|
- 如何在Angularjs2(2.0.1)中更快地加载Observable rxjs / Rx
- angularjs – Angular JS – 使用Karma / Jasmine测试页面重
- shell学习二-----变量基本知识
- bash – 跟踪gnu parallel中的状态/进度
- Angular ui.bootstrap.pagination 分页
- bootstrap datetimepicker的时间变成1899年
- Kafka – Docker – 从主机发送邮件到容器时发生错误(批
- 如何使用Twitter的Bootstrap popovers的jQuery验证通知?
- scala – 可以访问Option容器的选项操作符
- 【Angular】——BootStrap+Swiper实现手机端滑动窗口