垃圾 collection 在使用 ng-repeat AngularJS 构建大型 table 后在 Internet Explorer 中需要很长时间

Garbage collection takes a very long time in Internet Explorer after building a large table with ng-repeat AngularJS

我正在使用 AngularJS (1.2.26) 中的 ng-repeat 指令构建大型 table(3,000 多行)。数据是一个非常简单的时间和温度数组数组[[time, temperature], [time, temperature], ...]

这是数据在 table 中的显示方式(使用 Slim templating):

  table.table.table-bordered
    thead
      tr
        th.time.first-child= t('Time')
        th.temperature.sensor
    tbody
      tr[bindonce ng-repeat="item in readings"]
        td.datetime
          span.date bo-text="formatDate(item.time)"
          span.time bo-html="formatTime(item.time)"
        td bo-text="item.reading.temperature | number: 2" 

如您所见,我们正在使用 bind once 库来减少 angular 观察者的数量。这是一份报告,因此我们不需要维护数据绑定。

加载数据和呈现报告在 Chrome 或 Firefox 上大约需要 4 秒,在 Internet Explorer (11) 上大约需要 8 秒。但是,在 Internet Explorer 上构建 table 后,浏览器最多需要两分钟来执行垃圾处理 Collection(根据 UI 响应选项卡)。

我需要更多 "reputation" 到 post 图片,所以我会尽我所能描述发生的事情。

整个 table 渲染过程是 xhr.onreadystatechange 事件侦听器的结果(此后已从 1.3 中的 angular 中删除)。每行和跨度都添加到 table 中,使用来自 jQuery 的 appendChild() -> insertBefore() 函数或来自浏览器的 innerHTML 函数。这些发生了数千次,占了我之前提到的 ~8 秒。这些完成后,紧随其后的是几行垃圾 collection,总共需要 60 多秒。

在没有输出图片的情况下,这是在最后一个 appendChild() 之后开始执行的时间列表,后跟总执行时间(window).

  1. 0.24 秒,0.0065 毫秒
  2. 0.36 秒,13.83 毫秒
  3. 0.85 秒,0.31 毫秒
  4. 10 秒,0.0042 毫秒
  5. 57 秒,25.54 毫秒
  6. 60 秒,0.048 毫秒

然后,在剩下的过程继续之前,还有大约 10 秒的间隔。

我不明白这是怎么回事,因为每个垃圾collection很短,但他们之间的差距却很长。我不太了解浏览器中的垃圾collection,所以我妨碍了我的理解。 Chrome 或 Firefox 中不会发生这种情况。

我遵循了有关分析内存分析的各种指南。我不相信有内存泄漏。同样,由于我的声誉,我无法添加更多链接,但我遵循了标题为 "JavaScript Memory Profiling" 的 Chrome 和标题为 "Improving UI Responsiveness".

的 IE。

我不确定其中的另一部分是否相关,但我会在这里提及以防万一。当您在此延迟期间 运行 分析 session 时,IE 表示延迟发生在 AngularJS $$RAFProvider 的 requestAnimationFrame https://github.com/angular/angular.js/blob/master/src/ng/raf.js#L15 函数期间。这可能与 AngularJS 通过 ng-repeat 添加的每一行都会触发一个 "enter" 事件有关。到目前为止,我一直在尝试禁用该动画事件,但没有成功。

非常感谢任何建议或疑难解答提示。具体来说,如果我可以在垃圾 collection 上添加一个断点以查看发生了什么,那就太好了,但我认为我不能那样做。

对于 ng-repeat,angular 将为双向绑定中的每个项目创建一个范围。但是在你的代码中,没有必要,你只是显示数据。

如果您使用的是上述 angularjs 1.3.x,您可以使用 binging-once 功能,然后不会创建这些范围。

我不熟悉那个模板。但是你在TR里用'bindonce',如果在angularjs里是'bindonce',我觉得你应该用1.3.x版本。

还有一个问题是:

span.date bo-text="formatDate(item.time)"

您对数据格式使用了一个函数。而如上问题,你应该使用'bind-once'。如果用js的话就是:

<span>{{::formatDate(item.time)}}</span>

为绑定一次添加'::'。

如果您不添加一次绑定,当您滚动 table 时,同一项目的格式函数将被调用多次。所以对于格式,你应该使用像这样的日期过滤器:

<span>{{item.time | date: 'yyyy/MM/DD'}}</span>

经过大量调查,我认为我弄清楚了问题所在,或者至少是一个潜在的问题。

我正在渲染的 table 有两列 - 时间和温度。时间列有两个 <span> 元素。因此,对于每一行,Angular 都在为 "enter" 动画标记元素,就像它为 ng-repeat 的每个元素所做的那样。作为 Angular 动画过程的一部分,Angular 通过 $RAFProvider 从浏览器请求动画帧。当我在 Internet Explorer 中分析该过程并查看调用堆栈时,这就是巨大的延迟所在。我最初认为这不是问题所在,但经过大量故障排除后,我开始认为 Internet Explorer requestAnimationFrame 方法无法像 Chrome 或 Firefox 一样快速地处理 ~9000 次调用。

为了解决这个问题,我选择实施 ngInfiniteScroll 并且效果很好。如果用户真的想要完整的报告,我们将通过不同的代码路径生成完整的 3,000 行报告。