ng-repeat如何工作?

pil*_*lau 8 angularjs angularjs-ng-repeat

我解剖了ng-repeat并提取了附加的代码块,看到它们包含处理重复算法的逻辑(我想了解它是如何工作的).

我有很多问题,但由于它们都是关于ng-repeat的内部结构,所以我选择在这里问问它们.我认为没有理由将它们分成不同的SO问题.我已将每个问题引用的代码行标记为内联.

  1. 为什么他们需要确保这trackById不是本机hasOwnProperty功能?(这就是该assertNotHasOwnProperty函数的功能,是Angular内部API的一部分)
  2. 就我的直觉而言,这个代码在转发器中已有的项目上执行,当它必须更新集合时 - 它只是将它们拾取并将它们推入列表进行处理,对吧?
  3. 此代码块显然在转发器集合中查找重复项.但它究竟是如何做到这一点超出了我.请解释.
  4. 为什么角有存储块对象都nextBlockMap nextBlockOrder
  5. 什么是block.endNodeblock.startNode
  6. 我假设上述问题的答案将澄清这个算法是如何工作的,但请解释为什么它必须检查是否nextNode已经(已)'$$NG_REMOVED'
  7. 这里发生了什么?同样,我认为问题6已经为这一个提供了答案.但仍然指出了这一点.

就像我说的那样,我挖掘ng-repeat来找到我认为与重复机制相关的代码.另外,我理解指令的其余部分.所以不用多说,这里是代码(来自v1.2.0):

      length = nextBlockOrder.length = collectionKeys.length;
      for (index = 0; index < length; index++) {
       key = (collection === collectionKeys) ? index : collectionKeys[index];
       value = collection[key];
       trackById = trackByIdFn(key, value, index);

       // question #1
       assertNotHasOwnProperty(trackById, '`track by` id');

       // question #2
       if (lastBlockMap.hasOwnProperty(trackById)) {
         block = lastBlockMap[trackById];
         delete lastBlockMap[trackById];
         nextBlockMap[trackById] = block;
         nextBlockOrder[index] = block;

       // question #3
       } else if (nextBlockMap.hasOwnProperty(trackById)) {
         // restore lastBlockMap
         forEach(nextBlockOrder, function(block) {
           if (block && block.startNode) lastBlockMap[block.id] = block;
         });
         // This is a duplicate and we need to throw an error
         throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}",
                                                                                                                                                expression,       trackById);

       // question #4
       } else {
         // new never before seen block
         nextBlockOrder[index] = { id: trackById };
         nextBlockMap[trackById] = false;
       }
     }


      for (index = 0, length = collectionKeys.length; index < length; index++) {
        key = (collection === collectionKeys) ? index : collectionKeys[index];
        value = collection[key];
        block = nextBlockOrder[index];


        // question #5
        if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode;

        if (block.startNode) {
          // if we have already seen this object, then we need to reuse the
          // associated scope/element
          childScope = block.scope;

          // question #6
          nextNode = previousNode;
          do {
            nextNode = nextNode.nextSibling;
          } while(nextNode && nextNode[NG_REMOVED]);
          if (block.startNode != nextNode) {
            // existing item which got moved
            $animate.move(getBlockElements(block), null, jqLite(previousNode));
          }
          previousNode = block.endNode;

        } else {
          // new item which we don't know about
          childScope = $scope.$new();
        }

        // question #7
        if (!block.startNode) {
          linker(childScope, function(clone) {
            clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
            $animate.enter(clone, null, jqLite(previousNode));
            previousNode = clone;
            block.scope = childScope;
            block.startNode = previousNode && previousNode.endNode ? previousNode.endNode : clone[0];
            block.endNode = clone[clone.length - 1];
            nextBlockMap[block.id] = block;
          });
        }
      }
      lastBlockMap = nextBlockMap;
Run Code Online (Sandbox Code Playgroud)

pil*_*lau 10

经过一些修改指令后,我熟悉了ng-repeater代码,并设法回答了我的一些问题.我强调了大胆的事情我还不能找出我自己,如果任何人都可以对一些线索将不胜感激大胆部分:

  1. 测试ID hasOwnProperty,因为它们使用该方法来检查迭代对象中是否存在ID(lastBlockMap,nextBlockMap)(此过程如下所述).然而,我无法找出实际发生的情况.
  2. 我的假设是正确的.nextBlockMap包含将在当前模型更改中转换的所有项目.lastBlockMap包含先前模型更新中的所有内容.它用于在集合中查找重复项.
  3. 好的,这个实际上很简单.在这个for循环中,ng-repeat填充nextBlockMap来自的项目lastBlockMap.查看ifs 的顺序,很容易看出如果找不到该项lastBlockMap,但它已经存在nextBlockMap(意味着它已经从那里复制过来lastBlockMap,因此它trackById在集合中出现两次) - 它是重复的.什么forEach确实是简单地通过全部初始化项目运行nextBlockMap(blockS作一个startNode属性),推动其IDlastBlockMap.但我无法理解为什么这是必要的.
  4. 我可以找到分离nextBlockOrder(trackById数组中的所有s)和nextBlockMap(散列中的所有block对象)的唯一原因trackById是这一行,它使用数组使它变得简单易行:if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode;.问题5和6的答案中对此进行了解释:
  5. block.startNode并且block.endNode是属于所收集的项目的块中的第一个和最后一个DOM节点被重复.因此,此行设置previousNode为引用转发器中上一项的最后一个DOM节点.
  6. previousNode 然后用作第一个节点,在一个循环中检查当项目被移动或从转发器集合中移除时DOM如何改变 - 再次,仅在我们不使用数组中的第一个块的情况下.
  7. 这是很容易-它初始化块-分配$scopestartNodeendNode供以后参考,并保存一切nextBlockMap.在克隆元素之后创建的注释,是为了保证我们总是有一个endNode.