IE11中ng-repeat结合日期范围过滤器性能异常缓慢求助
首先得明确问题根源:IE11的Date.parse性能远低于现代浏览器,而你的dateRange过滤器在每次digest循环中,都会对8500条数据的每条记录重复执行3次Date.parse(解析startDate、endDate、当前记录的DateApplied),再加上AngularJS过滤器默认会在每次digest时重新执行,这直接导致了IE11的卡顿。下面是针对性的优化方案,按优先级排序:
1. 预解析所有日期为时间戳,彻底避免重复解析
这是见效最快的优化,核心是把Date.parse的执行次数从每次digest循环×8500×3降到仅当数据/日期条件变化时执行一次。
步骤1:预处理API返回的日期数据
在从Azure API获取到数据后,立即将每条记录的DateApplied转成时间戳并缓存到对象中,避免每次过滤都重复解析:
// 假设你在控制器中获取数据的逻辑 $http.get('your-azure-api-url').then(function(response) { $scope.applications = response.data.map(function(app) { // 提前解析日期为时间戳,后续直接使用 app.dateAppliedTimestamp = Date.parse(app.DateApplied); return app; }); });
步骤2:预解析筛选用的起止日期
在控制器中监听startDate和endDate的变化,提前转成时间戳,避免在过滤器里重复解析:
$scope.$watchGroup(['startDate', 'endDate'], function(newValues) { const [startVal, endVal] = newValues; // 处理空值:默认允许所有日期 $scope.parsedStart = startVal ? Date.parse(startVal.toString()) : -Infinity; $scope.parsedEnd = endVal ? Date.parse(endVal.toString()) : Infinity; });
步骤3:改造日期范围过滤器
现在过滤器可以直接使用预解析好的时间戳,完全去掉Date.parse调用,同时改用原生for循环(IE11中原生循环比angular.forEach快得多):
App.filter('dateRange', function () { return function (applications, startTs, endTs) { const filtered = []; const len = applications.length; // 用原生for循环替代angular.forEach,提升IE11性能 for (let i = 0; i < len; i++) { const app = applications[i]; if (app.dateAppliedTimestamp > startTs && app.dateAppliedTimestamp < endTs) { filtered.push(app); } } return filtered; }; });
步骤4:更新ng-repeat中的过滤器调用
把原来的dateRange: startDate:endDate改成使用预解析的时间戳:
<div class="inner" ng-repeat="app in displayedData = ( applications | orderBy: sortType:sortReverse | filter: FAl | filterStatus: FStatus | filterStage: FStage | dateRange: parsedStart:parsedEnd ) | start: (currentPage - 1) * perPage | limitTo: perPage"> <!-- 原有内容不变 --> </div>
2. 将多过滤器逻辑移到控制器中批量处理(进阶优化)
AngularJS的过滤器会在每次digest循环中逐个执行,即使你用了分页,前面的过滤器还是会先对8500条数据全部处理一遍。把所有过滤、排序、分页逻辑移到控制器中,只在筛选条件变化时执行一次,能进一步减少不必要的计算:
控制器中的批量处理逻辑
// 注入$filter服务 App.controller('YourController', function($scope, $http, $filter) { // 监听所有筛选条件的变化 $scope.$watchGroup(['sortType', 'sortReverse', 'FAl', 'FStatus', 'FStage', 'parsedStart', 'parsedEnd', 'currentPage', 'perPage'], function() { let processedData = [...$scope.applications]; // 1. 执行状态、阶段等过滤 processedData = processedData.filter(function(app) { return $scope.FStatus[app.Status] === true; // 对应filterStatus的逻辑 }); processedData = processedData.filter(function(app) { // 对应filterStage的逻辑,比如return $scope.FStage[app.Stage] === true; }); processedData = $filter('filter')(processedData, $scope.FAl); // 复用内置filter // 2. 日期范围过滤(用预解析的时间戳) processedData = processedData.filter(function(app) { return app.dateAppliedTimestamp > $scope.parsedStart && app.dateAppliedTimestamp < $scope.parsedEnd; }); // 3. 排序 processedData = $filter('orderBy')(processedData, $scope.sortType, $scope.sortReverse); // 4. 分页处理 const startIdx = ($scope.currentPage - 1) * $scope.perPage; $scope.displayedData = processedData.slice(startIdx, startIdx + $scope.perPage); }); });
简化ng-repeat
现在ng-repeat可以直接绑定处理好的displayedData,去掉所有过滤器链:
<div class="inner" ng-repeat="app in displayedData"> <div class="record" ng-class-odd="'odd odd-Record'" ng-class-even="'even even-Record'" data-toggle="collapse" data-target="#{{ app.Id }}"> <div class="field field-Name">{{ app.Lastname }}</div> <div class="field field-AccountNumber">{{ app.AccountNumber }}</div> <div class="field field-AppDate">{{ app.DateApplied | date : "dd/MM/yyyy"}}</div> </div> </div>
3. 额外的IE11专属优化
- 禁用IE11的兼容性视图:确保你的页面在IE11中以Edge模式运行,添加meta标签:
<meta http-equiv="X-UA-Compatible" content="IE=edge"> - 避免在ng-repeat中使用过多的表达式:你已经用了单向绑定
::,这点做得很好,继续保持。
这些优化后,IE11下的卡顿问题应该会完全解决——核心就是把昂贵的日期解析操作从“每次digest循环重复执行”变成“仅在必要时执行一次”,同时用更高效的原生循环替代框架内置的遍历方法。
内容的提问来源于stack exchange,提问作者Vereonix

