一尘不染

ng-repeat如何工作?

angularjs

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

我有很多问题,但是由于它们都是关于ng-
repeat的内部结构,因此我选择在这里全部询问。我看不出有任何理由将它们分为不同的SO问题。我已标记内联每个问题所指向的代码行。

  1. 为什么他们需要确保这trackById不是本机hasOwnProperty函数?(这就是该assertNotHasOwnProperty功能的作用,是Angular内部API的一部分)
  2. 就我的直觉而言,此代码在必须更新转发器的项目上对转发器中已有的项目执行-它只是将它们拾取并将其推入列表进行处理,对吗?
  3. 该代码块显然在转发器集合中查找重复项。但是,如何做到这一点超出了我。请解释。
  4. 为什么角有存储块对象都nextBlockMap nextBlockOrder
  5. 什么是block.endNodeblock.startNode
  6. 我认为上述问题的答案将阐明该算法的工作原理,但是请解释为什么它必须检查nextNodehas(been)'$$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;

阅读 334

收藏
2020-07-04

共1个答案

一尘不染

在修改了该指令之后,我开始熟悉ng-repeaters代码,并设法回答了我的一些问题。我以 粗体
突出显示了我自己还无法解决的问题,如果有人可以对 粗体 部分有所了解,我将不胜感激:

  1. 该ID用于测试hasOwnProperty,因为它们使用的方法来检查ID是否存在于迭代的对象(lastBlockMapnextBlockMap)(该过程在下面解释)。 但是,我无法确定在什么情况下会发生这种情况。
  2. 我的假设是正确的。nextBlockMap包含在当前模型更改中将被排除的所有项目。lastBlockMap包含上一次模型更新中的所有内容。它用于在集合中查找重复项。
  3. 好的,这实际上很简单。在此for循环中,ng-repeat填充的nextBlockMap项目lastBlockMap。查看ifs 的顺序,很容易看到,如果在中找不到该项目lastBlockMap,但它已经存在于中nextBlockMap(意味着它已经从中复制了lastBlockMap,因此trackById在集合中出现了两次)-它是重复项。什么forEach确实是简单地通过全部初始化项目运行nextBlockMapblockS作一个startNode属性),推动 其IDlastBlockMap但是,我不明白为什么这样做是必要的。
  4. 我可以找到将nextBlockOrdertrackById数组中的所有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. 这很容易-初始化块$scopestartNode并分配和和endNode供以后参考,并将所有内容保存在中nextBlockMap。在克隆元素之后立即创建的注释可以确保我们始终有一个endNode
2020-07-04