AngularJS'ng-filter'在大约1000个元素的阵列上非常慢

JVG*_*JVG 36 angularjs angularjs-ng-repeat angularjs-filter

我有一个简单的<input>搜索过滤器设置为项目名称列表AngularJS.

我的列表看起来像这样:

var uniqueLists = {
    category1: ['item1', 'item2', 'item3' ... 'item180' ], // Real list contains ~180 items
    category2: ['itemA', 'itemB', 'itemC' ... 'itemZZZ' ], // Real list contains ~1080 items
    category3: ['otheritem1', 'otheritem2', 'otheritem3' ]  // Real list contains 6 items
  }
Run Code Online (Sandbox Code Playgroud)

我在Angular中遍历此列表并在<ul>每个类别中打印出结果.

<div ng-repeat="(key,val) in uniqueLists">
    <form ng-model="uniqueLists[index][0]">
        <input ng-model="searchFilter" type="text" />
            <ul>
                <li ng-repeat="value in val | filter: searchFilter">
                    <label>
                         <input type="checkbox" ng-model="selectedData[key][value]" />
                        {{value}}
                    </label>
                </li>
            </ul>
    </form>
</div>
Run Code Online (Sandbox Code Playgroud)

为清楚起见,selectedData如下所示:

var selectedData = {category1: [item1:true], category2: [], category3: []); // if 'item1's checkbox is checked.
Run Code Online (Sandbox Code Playgroud)

这个列表运行得很好,虽然filter它非常滞后,即使在我非常快的计算机上也是如此.在输入中键入一个字母需要1-2秒才能更新列表.

我知道这可能是因为我一次过滤大约1000件物品,但我没有在其他地方看到过这方面的任何讨论.

有没有办法从过滤器中获得更好的性能?

Yos*_*shi 55

滤波器方法的主要问题是每次更改时都会操纵dom,因此滤波器不是缓慢但后果.另一种方法是使用类似的东西:

ng-show="([item] | filter:searchFilter).length > 0"
Run Code Online (Sandbox Code Playgroud)

关于重复的元素.

从@OverZealous借出一些代码,您可以使用以下内容来比较行为:


更新:使用Angular v1.2来了track by语法.这也有助于解决这些问题.如果元素具有一些唯一属性,则可以使用:

ng-repeat="item in items | filter:searchFilter track by item.id"
Run Code Online (Sandbox Code Playgroud)

item.id必须跨越所有项目唯一的.与track by只有那些以DOM元素将被移除这已经不再是在最后的名单,其他人将被铭记.而没有track by整个列表每次都重新绘制.简而言之:更少的dom操作=更快的重绘.

  • `ng-repeat ="item in items | filter:searchFilter track by item.id"`这导致性能提升0.我的数据已经是对象形式,具有唯一值,添加`track by depot.val`完全没有区别.我错过了什么吗? (3认同)
  • 非常感谢你这个伎俩:真的很棒. (2认同)

Tyv*_*ain 21

另一个有趣的优化是在一定时间之前"不触发"模型更改.

将其添加到您的搜索输入字段:ng-model-options ="{debounce:500}"

如果用户在500ms内停止输入,则会触发过滤器.

我更新了上面的小提琴:

http://jsfiddle.net/CXBN4/14/

<input ng-model="searchFilter" type="text" ng-model-options="{debounce: 500}" />
Run Code Online (Sandbox Code Playgroud)


Ove*_*ous 5

我创建了一个小提琴来模拟(部分)你正在展示的代码.

在我的计算机上,这是快速但不是超快,运行良好.它有点慢,但它过滤了一个过长的列表,它与复选框有双向绑定.每次键入字母时,都必须扫描整个列表并删除(或添加)项目.

我认为解决这个问题最好的办法是添加一些简单的分页,如StackOverflow的答案所示.

在这里,我修改了我的例子以包括分页.您可能希望投资一个比下一个/上一个更好的解决方案,但这会向您展示如果您不是一次性显示所有内容,结果会如何非常快.它仍然搜索整个列表,但渲染列表更加有限.

增加:

将分页信息添加到控制器中的范围:

$scope.currentPage = 0;
$scope.pageSize = 20;
$scope.numberOfPages = function () {
    return Math.ceil($scope.items.length / $scope.pageSize);
}
Run Code Online (Sandbox Code Playgroud)

创建一个从特定页面开始的新过滤器:

app.filter('startFrom', function () {
    return function (input, start, pageSize) {
        start = +start; //parse to int
        pageSize = +pageSize;
        while (start > input.length) {
            start -= pageSize;
        }
        if (start < 0) {
            start = 0;
        }
        return input.slice(start);
    };
});
Run Code Online (Sandbox Code Playgroud)

向视图添加过滤器以限制列表:

<li ng-repeat="value in items | filter:searchFilter |
        startFrom:currentPage*pageSize:pageSize | limitTo:pageSize">
Run Code Online (Sandbox Code Playgroud)

将分页按钮添加到页面:

    <div>
        <button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button> {{ currentPage+1 }}/{{ numberOfPages() }}
        <button ng-disabled="currentPage >= items.length/pageSize - 1" ng-click="currentPage=currentPage+1">Next</button>
    </div>
Run Code Online (Sandbox Code Playgroud)