提高嵌套 ng-repeat + 指令的性能

Posted

技术标签:

【中文标题】提高嵌套 ng-repeat + 指令的性能【英文标题】:Improving nested ng-repeat + directive performance 【发布时间】:2014-01-09 05:28:42 【问题描述】:

我有一个水平月历,显示员工每天的活动。

目前它正在使用具有以下层次结构/流程的表

1) 控制器:根据选择的月/年生成一个包含该月每一天的数组。只要年份或月份发生变化,$scope.$watchCollection 就会更新数组。

正如代码解释我做得更好:

<table class="table table-bordered table-condensed" ng-controller="PlanningOverviewController">
    <tr>
        <th><input type="text" ng-model="employeefilter"/></th>
        <th colspan="7" ng-repeat="week in weeks">
            week | weekheader
        </th>
    </tr>
    <tr>
        <th>&nbsp;</th>
        <th ng-repeat="day in days">
            day.format("ddd")
        </th>
    </tr>
    <tr>
        <th>&nbsp;</th>
        <th ng-repeat="day in days">
            day.date()
        </th>
    </tr>
    <tr ng-repeat="employee in planning | filter:employeefilter">
        <td> employee.firstName employee.lastName</td>
        <td class="calendar-unit" ng-repeat="day in days" ng-init="assignments = getAssignments(day, employee)">
            <div class="assignment"
                 assignment  ng-repeat="assignment in assignments"
                 assignment-description = "assignment.description"
                 assignment-period="assignment.period"
                 assignment-type="assignment.type">

            </div>
        </td>
    </tr>
</table>

问题在于 getAssignments() 是“昂贵的”,因为它需要检查:

    是公共假期吗? 员工当天上班吗? 员工放假了吗? 获取当天的项目任务

每当员工列表发生变化(由于过滤器)或月份发生变化时,当有大约 40 名员工时,表格的生成最多需要 6 秒。

有没有其他方法可以解决这个问题,或者改进 ng-repeats 的嵌套?

谢谢,如果需要更多信息,请澄清。

编辑:

我尝试从代码中删除一些元素,看看是什么导致了改进。当我让分配返回一个虚拟值时,渲染会提高 2-3 秒,因此那里的一些缓存可能会有所帮助。但是,以这种方式渲染“空”表仍然需要 3-4 秒。

然后我从指令本身中删除了逻辑,有效地使其为空,这仍然导致相同的延迟。

但是,当从 html 中删除指令本身时,表格几乎会在更改日期时立即呈现。我是否必须假设这是对角度的(正常?)限制?我不想要静态数据,因为目标是拖动分配。

更新 2: 我意识到我一直在低效地处理这个问题......在使用 batarang 查看范围和绑定 + 审查代码本身之后,我注意到我在做太多的“getAssignments()”检查,我的整个方法都是基于“为员工获取任务”,而专注于一天的任务可能会更好/更快。我会在周末更新代码并发布答案,如果它改进/解决了问题。

更新 3: 重写生成计划的方式后,现在需要 2 秒才能显示 50 个资源的表格。大约有 1750 条指令(每天至少有 1 个针对员工的事件)。我认为这是查看尺寸的正常延迟?

更新 4: 指令代码:

angular.module('planning.directives', ["templates.app", "ui.bootstrap", "planning.controllers"])
  .directive("assignment", function () 
    return 
        restrict: "EA",
        replace: true,
        scope: 
            assignmentData: "=",
            assignmentCreateMethod: "&"
        ,
        template: '<div class="assignment">assignmentData.description&nbsp;</div>',
        link: function (scope, element, attrs) 

            element.addClass(scope.assignmentData.type);

            if (scope.assignmentData.period == "am") 
                element.css("width", "50%");
                element.css("float", "left");
            

            if (scope.assignmentData.period == "pm") 
                element.css("width", "50%");
                element.css("float", "right");
            

            element.on("mouseenter",function () 
                element.addClass("hover");
            ).on("mouseleave", function () 
                    element.removeClass("hover");
                );

            element.on("click", function() 
                scope.assignmentCreateMethod();
            );
        
    ;
);

【问题讨论】:

瓶颈是重复调用getAssignments(),还是元素多ng-repeat慢?如果是第一种情况,你有没有考虑对getAssignments()应用一些缓存层? 重复调用,我不知道如何分析它,chrome分析器表明它一直在占用 $scope.digest,我自己的代码的第一个“昂贵”调用是getAssignments() 调用。我想我可以缓存它,但我觉得很奇怪,它只需要 6 秒来处理 40 行。是否可以在嵌套的 ng-repeats 中使用 Promise? 我删除了 getAssignments() 背后的逻辑,以便它总是为一天和员工返回相同的分配,现在它下降到 3-4 秒(对于只有这种大小的表来说仍然很慢?) 嵌套的 ng-repeat 是有问题的,在您的情况下,它有 3 个嵌套重复。那是你的瓶颈。如果您的某些数据是静态的(在该视图中不会更改),我建议您使用 bindonce。 如果您在本周末之后确实需要帮助,请提供一些最终结果的外观。 【参考方案1】:

在这种情况下,听起来您正在渲染更多可以随时可见的信息。所以你可以:

重新考虑带有某种分页的界面,这样您就可以在任何时候只渲染一个信息屏幕。或者您可以在任何时候显示的周数限制。

创建某种指令,如果它在屏幕上不可见,则删除其内容(即通过ngIf)。也许在窗口上监听(去抖动的)滚动事件,并从http://verge.airve.com/ 调用函数来测试它的任何部分是否可见。在这种情况下,对元素的垂直大小进行排序可能会很棘手,因此它稍微取决于您的用例。

为了澄清,这必须在 Angular 渲染之前删除内容,而不仅仅是隐藏元素。

【讨论】:

【参考方案2】:

嘿,“分配”是一个指令,对吧?也许你也应该在这里粘贴指令代码。

我曾经遇到过渲染延迟,是ng-bind-html引起的,不知道你用过没有。

【讨论】:

添加了,还没什么好看的。

以上是关于提高嵌套 ng-repeat + 指令的性能的主要内容,如果未能解决你的问题,请参考以下文章

ng-repeat动态创建嵌套ng-repeats的数组

在嵌套的 ng-repeat 中传递 2 个 $index 值

角度获取过滤后的 ng-repeat 的长度

嵌套 ng-repeat 拖放,AngularJS

为啥 ng-click 需要通过 $index 跟踪才能在嵌套的 ng-repeat 内触发

ng-repeat 嵌套 ng-switch 出错解决