AngularJS中oi-select加载大数据耗时过长的优化需求
解决AngularJS滚动加载自定义oi-select指令时的搜索范围问题
你已经实现了滚动加载的雏形,但搜索仅覆盖当前渲染数据的核心问题,本质是把过滤逻辑放在了已渲染的ng-repeat子集上,而非先在完整数据源过滤再分页。下面是一套完整的优化方案,既能解决搜索范围问题,还能进一步提升初始加载性能:
1. 重构数据结构:分离完整数据源与渲染子集
在directive作用域中维护三个关键数组,避免数据混淆:
fullOptionList: 存储全部3000条原始数据,仅初始化一次filteredOptionList: 存储经过搜索过滤后的完整结果数组displayedOptionList: 存储当前需要渲染的分页子集(滚动加载时动态扩展)
同时添加两个控制变量:
currentLimit: 当前渲染的条目数量(初始设为50)pageSize: 每次滚动加载新增的条目数量(比如50)
2. 优化初始数据处理:降低时间复杂度
你原来的双重angular.forEach匹配选中项,时间复杂度是O(n*m),数据量大时会非常慢。改成用Set快速匹配,把时间复杂度降到O(n+m):
// 初始化完整数据源 scope.fullOptionList = parentScope[scope.parentListName].sort(); scope.currentLimit = 50; scope.pageSize = 50; // 用Set存储选中项,快速匹配选中状态 var selectedItems = new Set(scope.optionListSelectedList); scope.tempOptionList = scope.fullOptionList.map(item => ({ val: item, check: selectedItems.has(item), originalcheck: selectedItems.has(item) })); // 初始状态下,过滤数据集等于完整数据集 scope.filteredOptionList = angular.copy(scope.tempOptionList);
3. 修改搜索逻辑:先过滤完整数据集,再分页渲染
当用户输入搜索关键词typeFilterOptions时,直接在完整数据集上执行过滤,更新filteredOptionList,然后重置currentLimit,确保从过滤结果的开头开始渲染:
scope.$watch('typeFilterOptions', function(newVal) { if (!newVal) { // 无搜索词时恢复为完整数据集 scope.filteredOptionList = angular.copy(scope.tempOptionList); } else { const lowerFilter = newVal.toLowerCase(); // 在完整数据集上执行过滤 scope.filteredOptionList = scope.tempOptionList.filter(item => { const targetContent = (item.label || item.val).toString().toLowerCase(); return targetContent.includes(lowerFilter); }); } // 重置渲染数量,从过滤结果的第一条开始显示 scope.currentLimit = scope.pageSize; });
4. 调整滚动加载逻辑:基于过滤后的数据集扩展渲染
修改addmoreitems函数,让它在filteredOptionList的基础上增加currentLimit,直到覆盖全部过滤结果:
scope.addmoreitems = function() { const remainingItems = scope.filteredOptionList.length - scope.currentLimit; if (remainingItems > 0) { // 每次新增pageSize条,剩余不足时取剩余数量 scope.currentLimit += Math.min(scope.pageSize, remainingItems); } };
5. 更新模板:简化渲染逻辑,减少性能开销
把ng-repeat的数据源改成filteredOptionList | limitTo: currentLimit,同时移除模板中的ng-if过滤条件(已在filteredOptionList中处理),并用一次性绑定减少watchers:
<div ng-repeat="optionVal in filteredOptionList | limitTo: currentLimit | orderBy : ['-originalcheck','+label'] track by optionVal.val" prevent-close> <label class="checkbox"> <input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check" ng-change="checking()"/> <span class="checkbox__input"></span> <span class="checkbox__label" style="color:#A9A9A9;">{{::optionVal.label || optionVal.val}}</span> </label> </div>
这里的track by避免重复渲染相同条目,{{::}}是一次性绑定,能大幅减少AngularJS的watchers数量,提升渲染流畅度。
额外性能优化建议
- 滚动监听事件要在directive销毁时解绑(用
$destroy事件),避免内存泄漏 - 如果
orderBy开销较大,可以在过滤后提前对filteredOptionList排序,而非在模板中使用过滤器 - 若数据更新频率低,可考虑用
$watchCollection替代$watch,减少不必要的digest循环
这样调整后,搜索会覆盖全部3000条数据,滚动加载也会基于搜索结果动态扩展,同时初始加载和渲染性能都会得到明显提升。
内容的提问来源于stack exchange,提问作者Sudarshan Kalebere
相关产品推荐
相关产品推荐

