$CompileProvider - acelan86/angular GitHub Wiki

$CompileProvider

function $CompileProvider($provide) {
  var hasDirectives = {};
  ...
  this.directive = function registerDirective(name, directiveFactory) {...};
  ...
  this.$get = [
                 '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
            '$controller', '$rootScope', '$document',
    function($injector,   $interpolate,   $exceptionHandler,   $http,   $templateCache,   $parse,
             $controller,   $rootScope,   $document) {

    
     ...


    return compile;

    function compile($compileNodes, transcludeFn, maxPriority) {

      var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority);

      return function publicLinkFn(scope, cloneConnectFn){
        var $linkNode = cloneConnectFn
          ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
          : $compileNodes;

        // 把scope连接到节点
        for(var i = 0, ii = $linkNode.length; i<ii; i++) {
          var node = $linkNode[i];
          if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
            $linkNode.eq(i).data('$scope', scope);
          }
        }
        //在链接节点上添加class ng-scope
        safeAddClass($linkNode, 'ng-scope');

        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
        //
        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
        return $linkNode;
      };
    }

    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
      var linkFns = [],
          nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;

      /** 
       * 下面这一坨就是从nodeList和他们的子节点中找出所有的指令相关的linkFn,放入nodeLinkFn,和childLinkFn中
       */
      for(var i = 0; i < nodeList.length; i++) {
        attrs = new Attributes();

        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
        directives = collectDirectives(nodeList[i], [], attrs, maxPriority);

        nodeLinkFn = (directives.length)
            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
            : null;

        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
            ? null
            : compileNodes(nodeList[i].childNodes,
                 nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);

        linkFns.push(nodeLinkFn);
        linkFns.push(childLinkFn);
        linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
      }
      /* 结束后的到linkFns数组,包含了所有的linkFn,注意,nodeLinkFn与它的childLinkFn连续放置,
       * 即linkFns = [nodeLinkFn1, childLinkFn1, nodeLinkFn2, childLinkFn2, ... ]
       * 并返回linkFnFound = true/false 
       */
 

      return linkFnFound ? compositeLinkFn : null;
      
      //返回复杂链接的方法
      function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
        var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n;

        //拷贝nodelist, 你懂的
        var stableNodeList = [];
        for (i = 0, ii = nodeList.length; i < ii; i++) {
          stableNodeList.push(nodeList[i]);
        }
        
        //遍历由nodeList中找出来的linkFns,做下面的事情:
        //
        for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
          node = stableNodeList[n];
          nodeLinkFn = linkFns[i++];
          childLinkFn = linkFns[i++];

          if (nodeLinkFn) {
            //判断是否需要生成新的scope
            if (nodeLinkFn.scope) {
              childScope = scope.$new(isObject(nodeLinkFn.scope)); //为他的子节点新建一个scope域
              jqLite(node).data('$scope', childScope);
            } else {
              childScope = scope;
            }

            //是否穿透
            childTranscludeFn = nodeLinkFn.transclude;
            if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
              nodeLinkFn(childLinkFn, childScope, node, $rootElement,
                  (function(transcludeFn) {
                    return function(cloneFn) {
                      var transcludeScope = scope.$new();
                      transcludeScope.$$transcluded = true;

                      return transcludeFn(transcludeScope, cloneFn).
                          bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
                    };
                  })(childTranscludeFn || transcludeFn)
              );
            } else {
              //执行nodelinkFn
              nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
            }
          } else if (childLinkFn) {
            childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
          }
        }
      }
    }

    function collectDirectives(node, directives, attrs, maxPriority) { ... }
   
    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
      return nodeLinkFn;
      function addLinkFns(pre, post) { ... }
      function getControllers(require, $element) { ... }
      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { ... }
    }

    function addDirective(tDirectives, name, location, maxPriority) { ... }

    function compileTemplateUrl(directives, beforeTemplateNodeLinkFn, $compileNode, tAttrs, $rootElement, replace, childTranscludeFn) {
      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) { ... };
    }

    function assertNoDuplicate(what, previousDirective, directive, element) { ... }

    function addTextInterpolateDirective(directives, text) { ... }

    function addAttrInterpolateDirective(node, directives, value, name) { ... }

    function replaceWith($rootElement, $element, newNode) { ... }
  }];
}

查找linkFn的方法,collectDirectives

   /**
     * 在给定的节点上查找指令并放入排序的directives集合里面
     *
     * @param node Node to search.
     * @param directives An array to which the directives are added to. This array is sorted before
     *        the function returns.
     * @param attrs The shared attrs object which is used to populate the normalized attributes.
     * @param {number=} maxPriority Max directive priority.
     */
    function collectDirectives(node, directives, attrs, maxPriority) {
      var nodeType = node.nodeType,
          attrsMap = attrs.$attr,
          match,
          className;

      switch(nodeType) {
        case 1: /* Element */
          // use the node name: <directive> 节点有对应的<directive>,对应“E”类型
          addDirective(directives,
              directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority);

          // 属性上是否有相应的directive, 对应“A”类型
          for (var attr, name, nName, value, nAttrs = node.attributes,
                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
            attr = nAttrs[j];
            if (attr.specified) {
              name = attr.name;
              nName = directiveNormalize(name.toLowerCase());
              attrsMap[nName] = name;
              attrs[nName] = value = trim((msie && name == 'href')
                ? decodeURIComponent(node.getAttribute(name, 2))
                : attr.value);
              if (getBooleanAttrName(node, nName)) {
                attrs[nName] = true; // presence means true
              }
              addAttrInterpolateDirective(node, directives, value, nName);
              addDirective(directives, nName, 'A', maxPriority);
            }
          }

          // class上是否有相应的directive,对应“C”类型
          className = node.className;
          if (isString(className) && className !== '') {
            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
              nName = directiveNormalize(match[2]);
              if (addDirective(directives, nName, 'C', maxPriority)) {
                attrs[nName] = trim(match[3]);
              }
              className = className.substr(match.index + match[0].length);
            }
          }
          break;
        case 3: /* Text Node */
          //文本类型特殊处理,先不看吧~
          addTextInterpolateDirective(directives, node.nodeValue);
          break;
        case 8: /* Comment */
          //注释上是否有相应的directive, 对应“M”类型
          try {
            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
            if (match) {
              nName = directiveNormalize(match[1]);
              if (addDirective(directives, nName, 'M', maxPriority)) {
                attrs[nName] = trim(match[2]);
              }
            }
          } catch (e) {
            // turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value.
            // Just ignore it and continue. (Can't seem to reproduce in test case.)
          }
          break;
      }

      //优先级排序
      directives.sort(byPriority);
      return directives;
    }

directiveNormalize方法

var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; //正则,匹配见下表,去掉一些不该有的前缀,你懂的
/**
 * 下面所有方式的名称转换成'myDirective':
 *   my:DiRective
 *   my-directive
 *   x-my-directive
 *   data-my:directive
 */
function directiveNormalize(name) {
  return camelCase(name.replace(PREFIX_REGEXP, ''));
}
⚠️ **GitHub.com Fallback** ⚠️