【问题标题】:angular.js $apply bottleneckangular.js $apply 瓶颈
【发布时间】:2015-12-04 11:50:19
【问题描述】:

我正在使用实时数据在网页上使用 SVG 绘制一些线条。为了管理我使用 Angular.js 的数据并管理我使用 D3.js 的可视化。

我设置了一个角度控制器来保存数据(行)。数据由一些点数组(带有 x/y 坐标的字典)组成。有些行在初始化时是已知的,有些则根据实时数据更新。

我设置了一个包含 SVG 元素的角度指令 ('topView')。对于初始化时的每一行,我将其添加为路径:

var routeLeftLine = container.select("#routes").append("path");            
var routeLeftLineData = scope.val.route.left; // -> 1000+ points in there
routeLeftLine
            .attr("d", lineFunction(routeLeftLineData))
            .attr("stroke", "black")
            .attr("stroke-width", 1)
            .attr("fill", "none");

对于我想要不断更新的每一行 (1),我设置了一个角度指令,例如:

<surface-cable val="data.cable"></surface-cable>

data 是控制器上的数据对象,data.cable 是点数组。 该指令如下所示:

OCMSWeb.directive('surfaceCable', function ( /* dependencies */ ) {
return {
    restrict: 'AE', 
    scope: { 
        val: '='
    },
    templateNamespace: 'svg',
    replace: true,
    template: '<g/>',
    link: function (scope, element, attrs) {
        var cableLine = d3.select(element[0]).append("path");

        scope.$watch('val', function () {
            var cableLineData = simplify(scope.val, 1, false); // size grows in time

            cableLine
                .attr("d", lineFunction(cableLineData))
                .attr("stroke", "rgb(240,144,32)")
                .attr("stroke-width", 1)
                .attr("fill", "none");
        }, true);
    }
};
});

当我使用计时器更新数据时,该结构工作正常,更改会反映在 SVG 中。

当我增加一行中的点数(> 1000 ...我将来需要更多)时,问题就出现了(不变的线和更新的线都有这种效果)性能下降。线的更新变得非常缓慢,即使要重绘的元素还不包含很多元素。

我找不到原因。 SVG/d3/angular 会再次渲染 svg 中的所有元素吗?
我绑定数据的方式效率低吗?我应该一起跳过d3吗?

我试图分析 javascript 性能,大约 80-90% 的 CPU 时间似乎用于 angular $apply 的调用(我认为它会扫描 DOM 以查找更改?)。如果一个元素(行是 &lt;path&gt; 元素)有很多数据点,为什么 $apply 需要这么长时间?

【问题讨论】:

  • 你能展示一下小提琴或stmg吗?

标签: javascript angularjs d3.js svg


【解决方案1】:

在这种架构下,1000 行意味着每次您更改范围内的任何内容时都会进行 1000 条指令、1000 次监视和 1000 次值比较,无论这些值是否实际发生了变化。我怀疑这里的根本问题是您的 d3 代码,尽管不必要地重置笔画、笔画宽度和填充的属性肯定没有帮助。

一般来说,更好的方法是使用一个指令,该指令采用一组线并处理 SVG 中所有电缆路径的布局。如果您正在查看成千上万条路径,那么您可能希望查看在画布上而不是 SVG 上渲染它们。

【讨论】:

  • 我认为我对这种情况不够清楚:我有几行 (3) 是静态的,但包含大量点 (1000-100.000)。然后我有 1 行是动态的(1-100.000 点)并且需要更新。在程序开始时,动态线很短(1 点),但每当我为静态线使用超过 1000 点时,重绘就会变得非常慢。
  • 如果页面上只有这个指令一次,那么我不明白为什么这一定很慢。我认为您需要发布一个工作示例。
【解决方案2】:

虽然@ethan-jewett 没有完全回答我的问题,但你回答了 指出我正确的方向。

因为我将控制器的“数据”字典(包含静态和动态数据)链接到指令,所以我认为 Angular 确实会检查其中的所有值是否有变化。通过将我的静态数据移出这个“数据”字典,它不会被检查,并使这个设置变得更快。

当我增加动态数据的大小时,分析仍然会产生角度变慢,我认为这是出于相同的原因(角度需要检查所有数据是否有变化)。我不确定我将如何解决这个问题:我将研究 D3.js 是否具有更有效的机制来检测数据的变化,或者我会将我的长数组拆分为静态和动态部分(因为它们表示路径/电缆,实际上只有电缆的末端可以改变。在某些时候,电缆的大部分可以被认为是静态的。)。

【讨论】:

  • 您是否有其他方法可以知道您的数据何时发生变化?如果是这样,也许根本不要使用 Angular 的 watch 函数,因为当 deep watch 参数为真时遍历整个对象的效率相当低 - 而是让你的指令公开一个更新函数,并在你知道需要触发时调用它更新。
  • 我一直在寻找如何实现这一点,但我在从控制器更新指令时发现的只是手表的使用。你能解释一下指令暴露和更新函数是什么意思吗?
  • 你在指令范围内传入一个变量(使用'='映射),然后在指令中附加一个函数。然后可以从包含范围访问此函数。如果不清楚 - 最好使用 scope.$broadcast 和 scope.$on 来代替,因为它更简单。我会链接到 API 文档,但看起来他们暂时破坏了 API 文档网站...
  • 我明白你的意思。相反,我创建了一个标志变量,当数据更新时它的值会发生变化。我在这个变量上使用了一个浅表,这大大加快了这个过程。
猜你喜欢
  • 2011-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多