【问题标题】:nvd3 used with angular-nvd3 is slooooowwwww与 angular-nvd3 一起使用的 nvd3 是 slooooowwwww
【发布时间】:2026-01-14 00:50:02
【问题描述】:

我相信我有一个问题,可以通过我缺少的东西很容易地解决,但我似乎看不出实际问题是什么。我有一个应用程序,它每秒返回 5000 个点(1000 个 x,y 点的 5 个数组元素),我想在客户端使用 NVD3 进行更新。这是一个 AngularJS 应用程序,所以我使用 krispos angular-nvd3 指令。但是,它使整个应用程序陷入困境,而且根据 Chrome 的开发者工具捕获的时间线,该应用程序似乎在等待 d3_timer_step 返回 5-6 秒。

我认为这个问题是由于我们更新数据的方式造成的,但整个问题似乎与实际的 d3 部分有关。客户端的代码是

<nvd3 options="optionsRingdown" data="ringdownAvg" config="{refreshDataOnly:true}"></nvd3>

在控制器中,选项定义如下

$scope.options = {
    chart: {
      type: 'lineChart',
      height: 300,
      margin: {
        top: 20,
        right: 40,
        bottom: 60,
        left: 75
      },
      x: function(d) {
        return d.x;
      },
      y: function(d) {
        return d.y;
      },
      useInteractiveGuideline: false,
      yAxis: {
        tickFormat: function(d) {
          return d3.format('0.01f')(d);
        },
        axisLabel: 'Testing'
      },
      xAxis: {
        tickFormat: function(d) {
          return d3.time.format('%X')(new Date(d));
        },
        rotateLabels: -45
      },
      transitionDuration: 0,
      showXAxis: true,
      showYAxis: true
    }
  };

并且数据在下面的模板中定义

var ringdownT = [{
   values: [],
   key: 'Cell 0'
 }, {
   values: [],
   key: 'Cell 1'
 }, {
   values: [],
   key: 'Cell 2'
 }, {
   values: [],
   key: 'Cell 3'
 }, {
   values: [],
   key: 'Cell 4'
 }];

数据是通过使用以下方法从服务广播的函数调用更新的

function updateCRD(d){
   var dataOut = {
     "tauData": [],
     "rdFit": ringdownT,
     "rdAvg":ringdownT
   }
   for (k = 0; k < d.cell.length; k++) {
     dataOut.rdAvg[k].values = d.cell[k].avg_rd;
     dataOut.rdFit[k].values = d.cell[k].fit_rd;
   }

   return dataOut;
}

函数在广播中调用,使用如下(以 1 秒间隔广播)

$scope.$on('dataAvailable', function() {

    $scope.data = Data.crd;

    var data = updateCRD(Data.crd);

    $scope.tauData = data.tauData;
    $scope.ringdownAvg = data.rdAvg;
    $scope.ringdownFit = data.rdFit;
});

有没有人看到这里有明显错误的地方,或者我应该采取不同的做法?有没有我错过的选项?任何帮助都会很棒。

干杯,马特

【问题讨论】:

  • 如果是我,我会尝试完全绕过角度范围来处理这种流量,这样就不必执行摘要,让 d3 直接处理数据
  • 我没有关注这个查理 - 你是在建议我删除 nvd3 指令吗?
  • 是的,您可能需要自己动手。下一段要阅读的代码是nvd3.lineChart

标签: javascript angularjs nvd3.js angular-nvd3


【解决方案1】:

尝试在配置中添加deepWatchData: false 标志(这意味着该指令不会观察数据的更新)并通过api 更新图表:

<nvd3 options="optionsRingdown" data="ringdownAvg" api="apiRingdown" config="{refreshDataOnly:true, deepWatchData: false}"></nvd3>

该指令使用$watch(watchExpression, listener, [objectEquality]) 方法监视选项和复杂数据对象的任何更新。在我们的例子中,deepWatchDataobjectEquality 标志,同时查看图表数据以获取更新。

根据angulardocs,根据angular.equals函数判断watchExpression的不等式。并且为了保存对象的值以供以后比较,使用了 angular.copy 函数。因此,这意味着观看复杂的对象会对内存和性能产生不利影响。

仅在版本(1.0.2、1.0.3)中,此标志默认为false


然后,要更新图表,我们可以在您的控制器中使用apiRingdown.update 方法:

$scope.$on('dataAvailable', function() {

    $scope.data = Data.crd;

    var data = updateCRD(Data.crd);

    $scope.tauData = data.tauData;
    $scope.ringdownAvg = data.rdAvg;
    $scope.ringdownFit = data.rdFit;

    //this line updates the chart
    $scope.apiRingdown.update();
});

更新

在最新版本 [1.0.4+] 中添加了一些更新。现在标记deepWatchData 表示使用或不使用数据监视(它不像以前那样objectEquality)。而deepWatchData 默认为true。但是现在我们可以使用新标志deepWatchDataDepth: 2 管理$watch 深度,从而调节性能。使用这个标志,我们可以为数据指定一个变化检测策略(范围 $watch 深度):

0 - By Reference (the least powerful, but the most efficient)
1 - By Collection Items
2 - By Value (the most powerful, but also the most expensive; default value)

另外,refreshDataOnly 的标志默认为true

因此,更新后的标签元素可能如下所示:

<nvd3 options="optionsRingdown" data="ringdownAvg" api="apiRingdown" config="{deepWatchDataDepth: 0}"></nvd3>

demo

【讨论】:

  • 克里斯波,感谢您的回答。也许我完全不理解实现上的差异,但这里有一个 NVD3 定期获取大量数据的示例,NVD3 似乎没有问题 - plnkr.co/edit/oUTsyuvN4kdk23FyQErB。那么对于与此实现不同的 AngularJS,我究竟需要做什么。
  • 唯一的区别是指令有数据更新的观察者。如果这个观察者是活跃的,它会在每次 $digest 循环更新时被触发。这会导致内存泄漏。所以我们用deepWatchData: false 关闭它,然后通过$scope.api.update() 更新图表。我已经用 angularjs 更新了你的例子:plnkr.co/edit/PqepCg
  • 太棒了,@krispo - 你打败了我!我当前版本的 angular-nvd3 将deepWatchData 标志设置为true,所以我会试试这个。我会让大家知道结果是什么......
  • 谢谢,@krispo - 看起来好多了。浏览器仍然受到破坏(它有点滞后),但我可能缺少一些优化。干杯,米
【解决方案2】:

你在使用SVG吗? nvd3.lineChartSVG 是的,可能。如果是这样,@mbostock 会为您提供答案:http://bl.ocks.org/mbostock/1276463。使用canvas 而不是SVG 可以提高速度。

https://www.safaribooksonline.com/blog/2014/02/20/speeding-d3-js-checklist/ 上的大多数建议都非常可靠。

你是否每秒重绘所有 5000 个点?如果是这样,这是 webGL imo 的工作,而不是 nvd3。 canvas 可能足够快来做到这一点,如果画布不够快,那么我会坚持以前的答案。

它在d3_timer_step 上花费的时间百分比是多少?该函数会很慢是没有意义的,它可能会被调用很多次。实际上,d3_timer_frame 是由 d3_timer_step 调用的,这可能是实际的渲染代码,肯定会占用你所有的时间。尝试做canvas

可能的 nvd3 性能改进:

  1. 如果您还没有禁用useInteractiveGuideline,请务必禁用。

【讨论】:

  • 看来 svg 应该不会有问题,直到你在点数方面达到那里 - 我见过像 100k 这样的东西。 5k 点似乎并不多。渲染所有这些需要 5 到 6 秒?好像我以某种方式滥用框架。您发现设置有什么问题吗?
  • 你对使用 angular 的画布有什么建议吗?
  • 尝试将@mbostock 的画布代码作为纯 html 和 js 直接粘贴到您的 Angular 应用程序中。一旦你可以让它渲染,你就可以修改它以获得你的数据。
  • 当需要在纸上打印图表时,您可能不想使用canvas。只需尝试使用 canvas 而不是 svg 的库打印任何图表。