• angular源码分析:angular的源代码目录结构说明


    一、读源码,是选择“编译合并后”的呢还是“编译前的”呢?

    有朋友说,读angular源码,直接看编译后的,多好,不用管模块间的关系,从上往下读就好了。但是在我看来,阅读编译后的源码至少有两点不好。

    • 1.编译已经将所有的代码合并在一起了,这会丢失掉作者模块设计的思想,不利于理解代码架构的精髓,甚至想理解代码都很难。
    • 2.编译合并后的angular代码有2万多行,想要看完,早已望而生畏。相反阅读编译前的代码,可以一个模块一个模块的逐个击破。
      但是编译前的源码中的确文件一大堆,一开始靠不清楚都是拿来干嘛的,这可能是初学者源代码阅读一个很大的障碍。我今天要做的事就是把angular的angular的目录和文件的结构理清分享给大家。

    二、Gruntfile.js

    1.angular的编译合并以及测试使用的是grunt框架,所以首先应该从这个文件着手。

    查看其源代码。

    var files = require('./angularFiles').files;
    var util = require('./lib/grunt/utils.js');
    

    注意:在grunt开头引入了这个两个文件:angularFiles.js 和 ./lib/grunt/utils.js。

    2.angularFiles.js:

    var angularFiles = {
        'angularSrc': [...],
    
        'angularLoader': [...],
    
        'angularModules': {
        'ngAnimate': [...],
        'ngCookies': [...],
        'ngMessageFormat': [...],
        'ngMessages': [...],
        'ngResource': [...],
        'ngRoute': [...],
        'ngSanitize': [...],
        'ngMock': [...],
        'ngTouch': [...],
        'ngAria': [...]
      },
    
      'angularScenario': [...],
    
      'angularTest': [...],
    
      'karma': [...],
    
      'karmaExclude': [...],
    
      'karmaScenario': [...],
    
      "karmaModules": [...],
    
      'karmaJquery': [...],
    
      'karmaJqueryExclude': [...]
    };
    
     ...
    if (exports) {
      exports.files = angularFiles;
      exports.mergeFilesFor = function() {
        var files = [];
    
        Array.prototype.slice.call(arguments, 0).forEach(function(filegroup) {
          angularFiles[filegroup].forEach(function(file) {
            // replace @ref
            var match = file.match(/^@(.*)/);
            if (match) {
              files = files.concat(angularFiles[match[1]]);
            } else {
              files.push(file);
            }
          });
        });
    
        return files;
      };
    }
    
    

    可以看到,其中就是定义了一个数组(files,其中每个元素都是一个文件名的数组。我们可以轻易的发现,files其实将所有的angular源码给分组追踪起来了)和一个函数(mergeFilesFor,用于合并angularFiles中的元素)。

    3.将“/lib/grunt/utils.js”文件跳过,在Gruntfile.js中搜索 关键词 build,可以看到如下代码:

        build: {
          scenario: {...
          },
          angular: {
            dest: 'build/angular.js',
            src: util.wrap([files['angularSrc']], 'angular'),
            styles: {
              css: ['css/angular.css'],
              generateCspCssFile: true,
              minify: true
            }
          },
          loader: {...},
          touch: {...},
          mocks: {...},
          sanitize: {...},
          resource: {...},
          messageformat: {...},
          messages: {...},
          animate: {...},
          route: {...},
          cookies: {...},
          aria: {...},
          "promises-aplus-adapter": {...}
        },
    

    我们可以猜测,"angular: {...}"就是对应angular.js的编译配置,而"loader: {...}"对应的是angular-loader.js,"touch: {...}"对应的是angular-touch.js,……
    那么,我们重点分析angular.js的编译配置,其他的都是类似的。

          angular: {
            dest: 'build/angular.js',
            src: util.wrap([files['angularSrc']], 'angular'),
            styles: {
              css: ['css/angular.css'],
              generateCspCssFile: true,
              minify: true
            }
          },
    

    4.util.wrap是什么鬼。从var util = require('./lib/grunt/utils.js');,我们知道应该阅读./lib/grunt/utils.js。

    果然其中有一个导出的函数wrap

      wrap: function(src, name){
        src.unshift('src/' + name + '.prefix');
        src.push('src/' + name + '.suffix');
        return src;
      },
    

    另外还有一个build函数,主要功能是合并js代码

      build: function(config, fn){
        var files = grunt.file.expand(config.src);
        var styles = config.styles;
        var processedStyles;
        //concat
        var src = files.map(function(filepath) {
          return grunt.file.read(filepath);
        }).join(grunt.util.normalizelf('
    '));
        //process
        var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
        if (styles) {
          processedStyles = this.addStyle(processed, styles.css, styles.minify);
          processed = processedStyles.js;
          if (config.styles.generateCspCssFile) {
            grunt.file.write(removeSuffix(config.dest) + '-csp.css', CSP_CSS_HEADER + processedStyles.css);
          }
        }
        //write
        grunt.file.write(config.dest, processed);
        grunt.log.ok('File ' + config.dest + ' created.');
        fn();
    
        function removeSuffix(fileName) {
          return fileName.replace(/.js$/, '');
        }
      },
    

    通过简单推理,build函数对代码的合并时根据配置数组的顺序粘贴完成的,而angular所包含的文件如下:

      'angularSrc': [
        'src/angular.prefix',  //util.wrap函数加入的前缀代码
    
        'src/minErr.js',
        'src/Angular.js',
        'src/loader.js',
        'src/stringify.js',
        'src/AngularPublic.js',
        'src/jqLite.js',
        'src/apis.js',
    
        'src/auto/injector.js',
    
        'src/ng/anchorScroll.js',
        'src/ng/animate.js',
        'src/ng/animateCss.js',
        'src/ng/browser.js',
        'src/ng/cacheFactory.js',
        'src/ng/compile.js',
        'src/ng/controller.js',
        'src/ng/document.js',
        'src/ng/exceptionHandler.js',
        'src/ng/forceReflow.js',
        'src/ng/http.js',
        'src/ng/httpBackend.js',
        'src/ng/interpolate.js',
        'src/ng/interval.js',
        'src/ng/locale.js',
        'src/ng/location.js',
        'src/ng/log.js',
        'src/ng/parse.js',
        'src/ng/q.js',
        'src/ng/raf.js',
        'src/ng/rootScope.js',
        'src/ng/sanitizeUri.js',
        'src/ng/sce.js',
        'src/ng/sniffer.js',
        'src/ng/templateRequest.js',
        'src/ng/testability.js',
        'src/ng/timeout.js',
        'src/ng/urlUtils.js',
        'src/ng/window.js',
        'src/ng/cookieReader.js',
    
        'src/ng/filter.js',
        'src/ng/filter/filter.js',
        'src/ng/filter/filters.js',
        'src/ng/filter/limitTo.js',
        'src/ng/filter/orderBy.js',
    
        'src/ng/directive/directives.js',
        'src/ng/directive/a.js',
        'src/ng/directive/attrs.js',
        'src/ng/directive/form.js',
        'src/ng/directive/input.js',
        'src/ng/directive/ngBind.js',
        'src/ng/directive/ngChange.js',
        'src/ng/directive/ngClass.js',
        'src/ng/directive/ngCloak.js',
        'src/ng/directive/ngController.js',
        'src/ng/directive/ngCsp.js',
        'src/ng/directive/ngEventDirs.js',
        'src/ng/directive/ngIf.js',
        'src/ng/directive/ngInclude.js',
        'src/ng/directive/ngInit.js',
        'src/ng/directive/ngList.js',
        'src/ng/directive/ngModel.js',
        'src/ng/directive/ngNonBindable.js',
        'src/ng/directive/ngOptions.js',
        'src/ng/directive/ngPluralize.js',
        'src/ng/directive/ngRepeat.js',
        'src/ng/directive/ngShowHide.js',
        'src/ng/directive/ngStyle.js',
        'src/ng/directive/ngSwitch.js',
        'src/ng/directive/ngTransclude.js',
        'src/ng/directive/script.js',
        'src/ng/directive/select.js',
        'src/ng/directive/style.js',
        'src/ng/directive/validators.js',
        'src/angular.bind.js',
        'src/publishExternalApis.js',
        'src/ngLocale/angular-locale_en-us.js',
    
        'src/angular.suffix'   //util.wrap函数加入的后缀代码
      ],
    

    5.分析下angular.prefix 和 angular.suffix

    angular.prefix

    /**
     * @license AngularJS v"NG_VERSION_FULL"
     * (c) 2010-2015 Google, Inc. http://angularjs.org
     * License: MIT
     */
    (function(window, document, undefined) {
    

    angular.suffix

      jqLite(document).ready(function() {
        angularInit(document, bootstrap);
      });
    
    })(window, document);
    

    很熟悉吧,angular.prefix 和 angular.suffix 刚好构成一个自动执行的匿名函数。在这个函数的最后做了一个操作,等待jqLite准备好后,执行angularInit(document, bootstrap);,如果我们今天是分析angular的代码可能就会继续去追jqLite函数以及angularInit函数。但是今天不是,我们的目标是树立代码目录结构。其实目录结构已经出来了。

    三、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的各种服务的目录,该目录下一个文件按名字对应一个服务  
    │    ├─── anchorScroll.js  
    │    ├─── animate.js  
    │    ├─── animateCss.js  
    │    ├─── browser.js  
    │    ├─── cacheFactory.js  
    │    ├─── compile.js  
    │    ├─── controller.js  
    │    ├─── document.js  
    │    ├─── exceptionHandler.js  
    │    ├─── forceReflow.js  
    │    ├─── http.js  
    │    ├─── httpBackend.js  
    │    ├─── interpolate.js  
    │    ├─── interval.js  
    │    ├─── locale.js  
    │    ├─── location.js  
    │    ├─── log.js  
    │    ├─── parse.js  
    │    ├─── q.js  
    │    ├─── raf.js  
    │    ├─── rootScope.js  
    │    ├─── sanitizeUri.js  
    │    ├─── sce.js  
    │    ├─── sniffer.js  
    │    ├─── templateRequest.js  
    │    ├─── testability.js  
    │    ├─── timeout.js  
    │    ├─── urlUtils.js  
    │    ├─── window.js  
    │    ├─── cookieReader.js  
    │    ├─── filter.js  
    │    ├─── filter         //过滤器目录,该目录下一个文件对应一个过滤器  
    │    │     ├─── filter.js  
    │    │     ├─── filters.js  
    │    │     ├─── limitTo.js  
    │    │     └─── orderBy.js  
    │    └─── directive      //指令目录,该目录下一个文件对应一个angular指令  
    │          ├─── directives.js  
    │          ├─── a.js  
    │          ├─── attrs.js  
    │          ├─── form.js  
    │          ├─── input.js  
    │          ├─── ngBind.js  
    │          ├─── ngChange.js  
    │          ├─── ngClass.js  
    │          ├─── ngCloak.js  
    │          ├─── ngController.js  
    │          ├─── ngCsp.js  
    │          ├─── ngEventDirs.js  
    │          ├─── ngIf.js  
    │          ├─── ngInclude.js  
    │          ├─── ngInit.js  
    │          ├─── ngList.js  
    │          ├─── ngModel.js  
    │          ├─── ngNonBindable.js  
    │          ├─── ngOptions.js  
    │          ├─── ngPluralize.js  
    │          ├─── ngRepeat.js  
    │          ├─── ngShowHide.js  
    │          ├─── ngStyle.js  
    │          ├─── ngSwitch.js  
    │          ├─── ngTransclude.js  
    │          ├─── script.js  
    │          ├─── select.js  
    │          ├─── style.js  
    │          └─── validators.js  
    ├─── angular.bind.js           //简单几行代码,判断是否已经加载了jQuery,如果是,直接使用jQuery,而不使用jqLite  
    ├─── publishExternalApis.js        // `publishExternalAPI(angular);`导出变量和接口  
    ├─── ngLocale  
    │       └───angular─locale_en─us.js //本地化处理,其中定义地区性的星期月份神马的  
    │  
    └── angular.suffix   //util.wrap函数加入的后缀代码
    

    上一期:angular源码分析:angular中各种常用函数,比较省代码的各种小技巧
    下一期:angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了
    ps:在这《angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了》这一期中,我将讲述笔者在其中遇到的一个坑。

  • 相关阅读:
    1.8 Hello World添加menu
    1.7 HelloWorld 添加视图
    1.6 Hello World
    1.5 组件开发基础
    awk
    sed
    grep / egrep
    Shell基础知识
    和管道符有关的命令
    Shell变量
  • 原文地址:https://www.cnblogs.com/web2-developer/p/angular-4.html
Copyright © 2020-2023  润新知