5

我有一個ng-repeat,它載入了數千條記錄,其複雜度可能高達100px到1200px之間。毋庸置疑,表現會受到相當大的衝擊。AngularJS無限滾動(ng-repeat) - 刪除DOM中的頂部元素

Infinite scrolling模塊在大多數情況下都可以正常工作,直到您遇到邊緣情況,並且已經向下滾動靠近底部,並且大部分元素已加載到DOM中,這會將我帶回原點。

Angular-vs-repeat對我來說是完美的,但我還沒有想出如何計算每個元素的高度,因爲它們不是固定的。

這使我回到無限滾動。 我假設頂部元素(在視口上方)將被替換爲空的DIV,其計算高度等於它們的總高度總和,則性能不會成爲問題。向上滾動會使它們回到dom中,並減去空的DIV高度。

有沒有人解決過這個問題?有什麼建議麼?代碼片段會很棒。

+1

很難讓你幫沒有任何例子。你通過codepen/...重現了一個相似的情況嗎? –

+0

我一直在尋找相同的虛擬滾動技術,但從來沒有真正實現它,因爲這將是過度工程。我也有不同高度的項目,所以現有的插件需要修改。如果我們需要指出性能會受到影響,並且我需要實現虛擬滾動,那麼我肯定會非常仔細地研究上述指令+可能會引入其他功能,例如在頂部和底部保留1.5頁元素(人們可以按Home and End鍵)和當前查看項目。他們之間將是空的高度調整虛擬容器。 –

+0

Checkout vs-repeat演示頁面,它使用可變大小的元素進行演示http://kamilkp.github.io/angular-vs-repeat/#?tab=6 –

回答

0

ng-repeat由於與其綁定相關的開銷而導致長列表性能下降相當陡峭。我特別喜歡的一個注重性能的圖書館是ag-grid,它具有可變行高的an example。你可能會看到它是否適用於你的目的。

如果沒有任何東西看起來符合您的需求,您可以隨時自行滾動您的指令並自己處理DOM操作,就像我將下面的代碼片段放在一起。它並不涵蓋你提到的所有內容,但它包括無限滾動和刪除舊元素,用空的<div>取代它們的高度,而不使用ng-repeat。

angular.module('SuperList', []) 
 
    .controller('mainCtrl', ['$scope', '$compile', 
 
    function($scope, $compile) { 
 

 
     // Magic numbers 
 
     var itemsPerLoad = 4; 
 
     var thresholdPx = 1200; 
 
     var removeThresholdPx = 1600; 
 

 
     // Options to control your directive are cool 
 
     $scope.listOptions = { 
 
     items: [], 
 
     renderer: renderer, 
 
     threshold: thresholdPx, 
 
     removeThreshold: removeThresholdPx, 
 
     loadFn: loadNewItems 
 
     }; 
 

 
     // This function creates a div for each item in our dataset whenever 
 
     // it's called by the directive 
 
     function renderer(item) { 
 
     var itemElem = angular.element('<div></div'); 
 
     itemElem.css('height', item.height + 'px'); 
 
     itemElem.html(item.text); 
 
     return itemElem; 
 

 
     // If each row needs special angular behavior, you can compile it with 
 
     // something like the following instead of returning basic html 
 
     // return $compile(itemElem)($scope); 
 
     } 
 

 
     // This gets called by the directive when we need to populate more items 
 
     function loadNewItems() { 
 
     // Let's do it async like we're getting something from the server 
 
     setTimeout(function() { 
 
      for (var i = 0; i < itemsPerLoad; i++) { 
 
      // Give each item random text and height 
 
      $scope.listOptions.items.push({ 
 
       text: Math.random().toString(36).substr(2, Infinity), 
 
       height: Math.floor(100 + Math.random() * 1100) 
 
      }); 
 
      } 
 
      // Call the refresh function to let the directive know we've loaded 
 
      // We could, of course, use $watch in the directive and just make 
 
      // sure a $digest gets called here, but doing it this way is much faster. 
 
      $scope.listOptions.api.refresh(); 
 
     }, 500); 
 

 
     // return true to let the directive know we're waiting on data, so don't 
 
     // call this function again until that happens 
 
     return true; 
 
     } 
 
    } 
 
    ]) 
 
    .directive('itemList', function() { 
 
    return { 
 
     restrict: 'A', 
 
     scope: { 
 
     itemList: '=' 
 
     }, 
 
     link: function(scope, element, attrs) { 
 
     var el = element[0]; 
 
     var emptySpace = angular.element('<div class="empty-space"></div>'); 
 
     element.append(emptySpace); 
 

 
     // Keep a selection of previous elements so we can remove them 
 
     // if the user scrolls far enough 
 
     var prevElems = null; 
 
     var prevHeight = 0; 
 
     var nextElems = 0; 
 
     var nextHeight = 0; 
 

 
     // Options are defined above the directive to keep things modular 
 
     var options = scope.itemList; 
 

 
     // Keep track of how many rows we've rendered so we know where we left off 
 
     var renderedRows = 0; 
 

 
     var pendingLoad = false; 
 

 
     // Add some API functions to let the calling scope interact 
 
     // with the directive more effectively 
 
     options.api = { 
 
      refresh: refresh 
 
     }; 
 

 
     element.on('scroll', checkScroll); 
 

 
     // Perform the initial setup 
 
     refresh(); 
 

 
     function refresh() { 
 
      addRows(); 
 
      checkScroll(); 
 
     } 
 

 
     // Adds any rows that haven't already been rendered. Note that the 
 
     // directive does not process any removed items, so if that functionality 
 
     // is needed you'll need to make changes to this directive 
 
     function addRows() { 
 
      nextElems = []; 
 
      for (var i = renderedRows; i < options.items.length; i++) { 
 
      var e = options.renderer(options.items[i]); 
 
      nextElems.push(e[0]) 
 
      element.append(e); 
 
      renderedRows++; 
 
      pendingLoad = false; 
 
      } 
 
      nextElems = angular.element(nextElems); 
 
      nextHeight = el.scrollHeight; 
 

 
      // Do this for the first time to initialize 
 
      if (!prevElems && nextElems.length) { 
 
      prevElems = nextElems; 
 
      prevHeight = nextHeight; 
 
      } 
 
     } 
 

 
     function checkScroll() { 
 
      // Only check if we need to load if there isn't already an async load pending 
 
      if (!pendingLoad) { 
 
      if ((el.scrollHeight - el.scrollTop - el.clientHeight) < options.threshold) { 
 
       console.log('Loading new items!'); 
 
       pendingLoad = options.loadFn(); 
 

 
       // If we're not waiting for an async event, render the new rows 
 
       if (!pendingLoad) { 
 
       addRows(); 
 
       } 
 
      } 
 
      } 
 
      // if we're past the remove threshld, remove all previous elements and replace 
 
      // lengthen the empty space div to fill the space they occupied 
 
      if (options.removeThreshold && el.scrollTop > prevHeight + options.removeThreshold) { 
 
      console.log('Removing previous elements'); 
 
      prevElems.remove(); 
 
      emptySpace.css('height', prevHeight + 'px'); 
 

 
      // Stage the next elements for removal 
 
      prevElems = nextElems; 
 
      prevHeight = nextHeight; 
 
      } 
 
     } 
 
     } 
 
    }; 
 
    });
.item-list { 
 
    border: 1px solid green; 
 
    width: 600px; 
 
    height: 300px; 
 
    overflow: auto; 
 
} 
 
.item-list > div { 
 
    border: 1px solid blue; 
 
} 
 
.item-list > .empty-space { 
 
    background: #aaffaa; 
 
}
<html> 
 

 
<head> 
 
    <link rel="stylesheet" href="test.css"> 
 
</head> 
 

 
<body ng-app="SuperList" ng-controller="mainCtrl"> 
 
    <div class="item-list" item-list="listOptions"></div> 
 

 
    <script src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script> 
 
    <script src="test.js"></script> 
 
</body> 
 

 
</html>

+0

如果您滾動,您將如何解決/添加項目向後? – karma