【问题标题】:angularjs ng-model inside ng-repeat has poor performanceng-repeat 中的 angularjs ng-model 性能很差
【发布时间】:2014-08-30 09:08:15
【问题描述】:

在以下情况下,我遇到了 Angular 的性能问题:

<div ng-repeat="a in array">
  <input ng-model="something">
</div>

我在我的控制器中编写了代码,在ng-click 上将数组更改为具有一组不同的对象。问题是,如果数组有相当数量的对象,那么点击就没有我想要的那么灵敏(短暂的延迟)。

经过一番调查,我注意到在我更改 ng-click 中的数组后,$digest 需要很长时间。所以我创建了这个short test code 来重现它。

真正的应用场景是这样的:我有一个表格,其中每一行代表一个可编辑的对象,每个对象都有许多我希望能够编辑的不同字段。这样,每当我点击表格中的一行时,就会有另一个 html 包含所有 ng-repeats 和我的对象属性上的不同 inputs。

有人知道如何提高效率吗?

谢谢

【问题讨论】:

  • 创建一个真实的演示,展示您实际在做什么。当我们看不到任何代码时,谁能让您的代码更有效率?
  • 嗯,你是对的,但是我的例子太复杂了,不能放在这里解释,所以我试着把它缩小到真正的问题,那就是:为什么这比当我删除 ng-model 属性时(我理解开销,但不明白为什么它应该这么大)
  • 但如果您看到视觉延迟,您可能会做一些不是最佳实践的事情
  • 我将尝试简要描述我的生产代码。我有一个画布,其中包含您可以选择和拖动的对象,并且此画布以某种方式限制为有角度的。当您开始拖动一个对象并且 cpu 甚至需要 50 毫秒才能运行时,您的应用程序会出现一个小的延迟,您会感觉到它比点击时感觉要多得多。发生这种情况的原因是因为当我开始拖动时 - 对象被选中(通过在范围内设置一个变量)和另一个观察该对象开始呈现自身的指令(这让我想到了我提出的问题),在另一条评论中继续...
  • 您是否尝试过在 Chrome 上使用batarang 来更好地了解为什么需要这么长时间?您可以分析性能并查看哪些特定任务比其他任务花费的时间更长。

标签: javascript performance angularjs


【解决方案1】:

在同一页面上有太多输入元素通常是个坏主意。这就是为什么专业的数据网格编辑器选择在单独的弹出窗口或内联中一次只编辑一个数据行。即使是在线使用,对象也是即时注入的。

一个输入元素太重了,不能在同一个页面上有太多的元素。我过去也犯过同样的错误,试图实现一个数据网格,其中所有可编辑字段从一开始就是输入元素。最重要的是,您必须保持实时的角度模型绑定,这会增加性能开销。

在您的情况下,最简单的方法之一是实现一个指令,该指令显示为 span 元素,直到它被单击并在单击事件时交换输入元素。另一种选择——同时拥有并切换它们的可见性样式。后者在 angular 指令中可能更容易,但效率可能不高。

还要留意您拥有的其他绑定。当涉及到数据网格时,这变得很重要。在 Angular 1.3 中,您现在可以使用“::”语法进行一次性绑定,这也可能会有所帮助。

【讨论】:

  • 这些选项似乎是解决性能问题的好方法。但是,您忽略了这样一个事实,即您将无法通过按 tabenter 从输入跳转到输入,这 - 特别是在这种有很多输入的情况下,是必不可少的。
  • @frhd 这就是一些库手动实现这一点的原因。可以在窗口级别处理 Tab 并重定向到您的自定义控件。
【解决方案2】:

如果我正确理解了您的问题,那么您遇到了 ui 阻塞,这让您的体验变得不稳定。您的收藏中有多少件物品?如果我没记错的话 ng-repeat 是同步的,如下所示: https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js#L361

因此,如果不使用当前的 ng-repeat 实现方式阻塞 UI,您将无法呈现数百/数千/等记录。唯一的解决方案是使用异步解决方案,一次渲染一点集合。不幸的是,我还没有看到异步 ng-repeat(可能存在,您应该自己寻找或实现它并提供给我们)。

幸运的是您可以使用 ng-repeat 的 limitTo 属性作为类似的 hack。如果您有 limitTo:num where num = 5 则只会呈现前 5 条记录。如果设置 num = 7 则保留已渲染的 5 条记录,仅渲染第 6 条记录和第 7 条记录。

所以要渲染几千条记录,您需要这样的东西来异步更改 limitTo:

<div ng-repeat="a in array | limitTo:tick">

...

var repeatAsyncHack = function() {
    $scope.$digest();
    sum += +new Date() - t;
    if($scope.tick < numOfObjects) {
        $scope.tick+=tickAmount;
        requestAnimationFrame(repeatAsyncHack);
    } else {
        $('#results').prepend(
            $("<div>" + 
              numOfObjects + ' objects test took: ' + sum +
              "ms</div>"));
    }
}

我在http://jsfiddle.net/qrJG3/15/更新了你的演示

当然,这也有缺点,因为总渲染时间要长得多。毕竟,这样做只是为了让应用响应更快。

如果我误解了你的意思,请纠正我。

【讨论】:

    【解决方案3】:

    好吧,如果您正在动态创建 DOM 元素,将会有延迟。

    如果元素已经在页面上并且您只是将它们隐藏到它们的时间它通常会更快。

    您可以使用的另一个方面是 felt responsivenessreal responsiveness - 您可以先显示一些静态 html,然后用实际元素替换它。在创建实际结果的同时,用户会立即获得结果。

    编辑

    让我们比较生成的 HTML 有和没有ng-model

    有模型

    <div ng-repeat="a in array" class="ng-scope">
       <input ng-model="a.qqqq" class="ng-pristine ng-valid">
    </div>
    

    在输入上加上事件处理程序。

    没有模型

    <div ng-repeat="a in array" class="ng-scope">
         <input>
    </div>
    

    而且没有事件处理程序。

    您可以使用 Angular 的调试版本进行深入研究,以实际查看添加 ng-model 时所做的事情。

    -> 花费更多时间的原因很明显 - Angular 做得更多。

    【讨论】:

    • 谢谢,但是动态创建 DOM 元素并不是这里的问题。如果您在我的示例中仅删除 ng-model,您会发现它的工作速度大大加快。看看这个:jsfiddle.net/c49sL
    • 添加很多时这是一个问题 - 通常一个简单的“请稍候..”是唯一的选择jsfiddle.net/Vfg8L
    • 所以问题是屏幕冻结了一段时间,我知道这可能是内容过多的问题,但我认为我有相当多的内容,但我没有看到一个很好的理由为什么 Angular 需要比添加普通输入元素多花 300% 的时间,只是看起来开销太大
    • 我改变了我的答案 - 这是 Angular 需要更多时间的原因吗?
    • 我理解为什么它需要更多的时间——我只是认为拥有 300% 的开销实在是太多了。尤其是当 Angular 在你用它做的许多其他事情中没有那种开销时。
    猜你喜欢
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 2015-08-13
    相关资源
    最近更新 更多