Angular-bootstrap -反复调用带有bootstrap工具提示的$compile会导致运行缓慢

angular-bootstrap - repeatedly calling $compile with bootstrap tooltip causes slowness

本文关键字:compile 缓慢 运行 工具提示 调用 bootstrap Angular-bootstrap      更新时间:2023-09-26

我有一个应用程序设置使用轮询一次,每秒更新饼状图指令。我使用d3来绘制饼状图,我想在饼状图上使用引导工具提示。为了实现这一点,我使用angular-bootstrap,并在每次数据更新时手动调用$compile来处理工具提示指令。它看起来像这样:

app.directive('piechart', ['$compile', function($compile) {
    return {
      restrict: 'E',
      scope: { data: '=', w: '=width', h: '=height', label: '=' },
      link: function(scope, element) {
        ...
        var svg = d3.select(element[0])
              .append('svg')
                .attr('width', scope.w)
                .attr('height', scope.h);
        scope.$watch('data', function(newValue) {
          if (newValue) {
            var pieData = pie(newValue);
            var arcs = svg.selectAll('g.arc')
              .data(pieData, function(d) { return d.data.label; });
            arcs.attr('tooltip', function(d, i) { 
                  return scope.data[i].label + ': ' + scope.data[i].value; 
                }).select('path')
                .transition()
                .delay(100)
                .duration(750)
                .attrTween('d', arcTween);
            var enteredArcs = arcs.enter()
              .append('g')
                .attr('class', 'arc')
                .attr('transform', 'translate(' + outerRadius + ',' + outerRadius + ')')
                .attr('tooltip-append-to-body', true)
                .attr('tooltip', function(d, i) { 
                  return scope.data[i].label + ': ' + scope.data[i].value; 
                });
            enteredArcs.append('path')
                .attr('fill', 'white')
                .attr('stroke', 'white')
                .attr('stroke-width', 2)
                .on('mouseover', function() { d3.select(this).transition().duration(500).attr('d', arcOver); }) 
                .on('mouseout', function() { d3.select(this).transition().duration(200).attr('d', arc); })
                .each(function(d) { this._current = d; }) // store the initial angles
                .transition()
                .delay(function(d, i) { return (i * 20) + 50; })
                .attr('fill', function(d) { return color(d.data.label); })
                .attr('d', arc);             
            arcs.exit().remove();
            $compile(svg[0])(scope);
          }
        }, true);
      }
    };
}]);

我注意到,在运行了一分钟左右后,它变得非常缓慢和无响应。如果我将鼠标移到元素上进行分析,我看到它在$interpolate.fn中花费了过多的时间。根据分析器,调用堆栈为:

hideTooltipBind -> Scope。$apply -> Scope。$digest -> $interpolate.fn

(其中hideTooltipBind在ui-bootstrap-tpls.js中)

我猜,在重复调用$compile,旧的工具提示没有清理?这是一个bug在angular引导或问题在我的代码?

编辑:似乎在重复调用$compile时,由第一次调用$compile创建的子范围不会被销毁,而$destroy是取消工具提示绑定的。不知道如何销毁旧的子上下文

我通过在调用$compile之前手动销毁指令作用域的所有子作用域来解决这个问题。因为这个指令使用了一个隔离的作用域,所以应该是安全的。如果谁有更好的主意,我很乐意听听。

代码:

var destroyer = function(scopeElem) {
  if(scopeElem.$$nextSibling) {
    destroyer(scopeElem.$$nextSibling);
  }
  scopeElem.$destroy();
};
if(scope.$$childHead) {
  destroyer(scope.$$childHead);
}
$compile(svg[0])(scope);