zoukankan      html  css  js  c++  java
  • angular源码分析:$compile服务——directive他妈

    一、directive的注册

    1.我们知道,我们可以通过类似下面的代码定义一个指令(directive)。

    var myModule = angular.module(...);
    
      myModule.directive('directiveName', function factory(injectables) {
        var directiveDefinitionObject = {
          priority: 0,
          template: '<div></div>', // or // function(tElement, tAttrs) { ... },
          // or
          // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
          transclude: false,
          restrict: 'A',
          scope: false,
          controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
          controllerAs: 'stringAlias',
          require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
          compile: function compile(tElement, tAttrs, transclude) {
            return {
              pre: function preLink(scope, iElement, iAttrs, controller) { ... },
              post: function postLink(scope, iElement, iAttrs, controller) { ... }
            }
            // or
            // return function postLink( ... ) { ... }
          },
          // or
          // link: {
          //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
          //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
          // }
          // or
          // link: function postLink( ... ) { ... }
        };
        return directiveDefinitionObject;
      });
    

    通过前面的分析(directive: invokeLater('$compileProvider', 'directive')),我们可以知道上面的代码会最终调用$compileProvider.directive

    2.$compileProvider.directive

    
      var hasDirectives = {},//定义用于存储指令的对象
          Suffix = 'Directive',
          COMMENT_DIRECTIVE_REGEXP = /^s*directive:s*([w-]+)s+(.*)$/,
          CLASS_DIRECTIVE_REGEXP = /(([w-]+)(?::([^;]+))?;?)/,
          ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
          REQUIRE_PREFIX_REGEXP = /^(?:(^^?)?(?)?(^^?)?)?/;
      /**
       * @ngdoc method
       * @name $compileProvider#directive
       * @kind function
       *
       * @description
       * Register a new directive with the compiler.
       *
       * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
       *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
       *    names and the values are the factories.
       * @param {Function|Array} directiveFactory An injectable directive factory function. See
       *    {@link guide/directive} for more info.
       * @returns {ng.$compileProvider} Self for chaining.
       */
       this.directive = function registerDirective(name, directiveFactory) {
        assertNotHasOwnProperty(name, 'directive');
        if (isString(name)) {
          assertValidDirectiveName(name);
          assertArg(directiveFactory, 'directiveFactory');
          if (!hasDirectives.hasOwnProperty(name)) {
            hasDirectives[name] = [];
            $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',//这里在定义指令的服务,比如注册了`test`这样的一个指令,这里就会定义个`testDirective`的服务.
              function($injector, $exceptionHandler) {//注意这个匿名函数,将在需要获取指令的是否被执行,所以当这个函数执行时,所有的指令的定义都放入了hasDirectives数组
                var directives = [];
                forEach(hasDirectives[name], function(directiveFactory, index) {//注意这里的directiveFactory是从数组中取出的元素而不是前面函数的参数
                  try {
                    var directive = $injector.invoke(directiveFactory);
                    if (isFunction(directive)) {
                      directive = { compile: valueFn(directive) };
                    } else if (!directive.compile && directive.link) {
                      directive.compile = valueFn(directive.link);
                    }
                    directive.priority = directive.priority || 0;
                    directive.index = index;
                    directive.name = directive.name || name;
                    directive.require = directive.require || (directive.controller && directive.name);
                    directive.restrict = directive.restrict || 'EA';
                    var bindings = directive.$$bindings =
                        parseDirectiveBindings(directive, directive.name);//这个parseDirectiveBindings需要分析
                    if (isObject(bindings.isolateScope)) {
                      directive.$$isolateBindings = bindings.isolateScope;
                    }
                    directive.$$moduleName = directiveFactory.$$moduleName;
                    directives.push(directive);
                  } catch (e) {
                    $exceptionHandler(e);
                  }
                });
                return directives;
              }]);
          }
          hasDirectives[name].push(directiveFactory);//这里可以看到指令可以同名,一个指令名对应的是一个指令数组
        } else {
          forEach(name, reverseParams(registerDirective));//可以数组的方式,成批量的注册指令
        }
        return this;
      };
    

    请注意代码的执行数序。
    a.在第一注册某个执行时(比如现在注册了两个test执行),那么第一次调用这个函数注册指令时,会定一个testDirective的服务,且将该指令的工厂函数压入hasDirectives['test']
    b.当再次注册一个与test同名的另一个指令时,仅是将其工厂函数压入hasDirectives['test']
    c.当指令需要时,框架会调用testDirectiveProvider.$get(也就是testDirective的工厂方法)制造一个directives数组

    3.parseDirectiveBindings

      function parseIsolateBindings(scope, directiveName, isController) {
        var LOCAL_REGEXP = /^s*([@&]|=(*?))(??)s*(w*)s*$/;
    
        var bindings = {};
    
        forEach(scope, function(definition, scopeName) {
          var match = definition.match(LOCAL_REGEXP);
    
          if (!match) {
            throw $compileMinErr('iscp',
                "Invalid {3} for directive '{0}'." +
                " Definition: {... {1}: '{2}' ...}",
                directiveName, scopeName, definition,
                (isController ? "controller bindings definition" :
                "isolate scope definition"));
          }
    
          bindings[scopeName] = {
            mode: match[1][0],
            collection: match[2] === '*',
            optional: match[3] === '?',
            attrName: match[4] || scopeName
          };
        });
    
        return bindings;
      }
    
      function parseDirectiveBindings(directive, directiveName) {
        var bindings = {
          isolateScope: null,
          bindToController: null
        };
        if (isObject(directive.scope)) {//指令对象中scope==true 或者 scope是一个对象时 
          if (directive.bindToController === true) {
            bindings.bindToController = parseIsolateBindings(directive.scope, //解析scope对象中的表达式
                                                             directiveName, true);
            bindings.isolateScope = {};
          } else {
            bindings.isolateScope = parseIsolateBindings(directive.scope,
                                                         directiveName, false);
          }
        }
        if (isObject(directive.bindToController)) {
          bindings.bindToController =
              parseIsolateBindings(directive.bindToController, directiveName, true);
        }
        if (isObject(bindings.bindToController)) {
          var controller = directive.controller;
          var controllerAs = directive.controllerAs;
          if (!controller) {
            // There is no controller, there may or may not be a controllerAs property
            throw $compileMinErr('noctrl',
                  "Cannot bind to controller without directive '{0}'s controller.",
                  directiveName);
          } else if (!identifierForController(controller, controllerAs)) {
            // There is a controller, but no identifier or controllerAs property
            throw $compileMinErr('noident',
                  "Cannot bind to controller without identifier for directive '{0}'.",
                  directiveName);
          }
        }
        return bindings;
      }
    
    

    二、给出一幅图说明angular的"编译原理"

    1.在定义或者注册指令,最终是以延迟调用$compileProvider.Directive来完成 2.编译阶段,主要工作是收集dom元素上引用到的指令,编译函数将返回一个"链接函数"用户完成和$scope的链接. 3.链接过程,将$scope与dom建立联系,指令指令中定义的link函数

    由于$compile这部分的代码过于复杂,本期暂且讲到这里,下期继续
    上一期:angular源码分析:angular中脏活累活的承担者之$interpolate
    下一期:angular源码分析:$compile服务——指令的编写

  • 相关阅读:
    Inception V1-V4
    NDCG的理解
    进程与线程
    Java中的接口和抽象类
    HashMap的工作原理
    基于比较排序的算法复杂度的下界
    数据库-left join,right join,inner join,full join
    外排序 External sorting
    数据流中的中位数 Find Median from Data Stream
    Codeforces Round #272 (Div. 2)
  • 原文地址:https://www.cnblogs.com/web2-developer/p/angular-13.html
Copyright © 2011-2022 走看看