我有一个ng-repeat,它可以加载数千个记录,其复杂程度可以达到100px到1200px之间。毋庸置疑,表现会受到很大影响。
在大多数情况下,Infinite scrolling模块可以正常工作,直到你遇到一个边缘情况,你已经向下滚动到底部并且大部分元素已经被加载到DOM中,这让我回到原点之一。
Angular-vs-repeat对我的情况来说是完美的,但我还没弄清楚如何计算每个后面元素的高度,因为它们没有固定。
让我回到无限滚动。 我假设如果顶部元素(在视口上方)将被替换为空DIV,其计算高度等于它们的总高度总和,则性能不会成为问题。向上滚动会将它们渲染回dom并减去空DIV的高度。
以前有人解决过这个问题吗?有什么建议?代码片段很精彩。
答案 0 :(得分:0)
ng-repeat
由于与其绑定相关的开销而导致长列表性能急剧下降。我特别喜欢的一个具有表现意识的图书馆是ag-grid,其中an example方便地具有可变的行高。{3}}您可能会看到它是否适用于您的目的。
如果没有任何内容似乎符合您的需求,您可以随时滚动自己的指令并自行处理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;
}
}
}
};
});
&#13;
.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;
}
&#13;
<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>
&#13;