2

我想在一个复杂的angular.js web应用程序中以树的形式实现分层结构。我正在使用嵌套的ng-repeats来呈现结构,但我在IE 10中遇到了与性能相关的重大问题,并且在Chrome中出现了一些次要的性能问题。将使用的数据将包含最终级别的多达5,000个条目。 根据我的研究,我认为以下可能是原因背后:守望元素如何在angularjs中处理树状结构?

  1. 大量。 为了解决这个问题,我已经实现了一次数据绑定,并且观察者的数量并不是那么高。
  2. 浏览器重绘时间: ng-repeat将元素逐个添加到DOM中。这可能导致浏览器引擎超载,导致复杂的HTML多次,导致大量的滞后。 为了解决这个问题,我通过仅在一个节点被折叠时渲染子节点来应用延迟加载排序技术。不过,我在呈现节点的渲染节点时出现可观察的延迟,其中渲染节点的数量很大。
  3. CSS类: 我尝试通过剥离节点元素的所有类来实现树结构。这导致了显着的改善,但删除班级并不是真正的选择。另外,如果我给内联风格的元素,那么它也会导致更好的性能。
  4. 角材料的性能问题: 角材料是我的网络应用程序的组成部分。在深入研究角色素材用户提交的大量ng-repeats所提交的问题后,已经推出了哪些修补程序。但升级到最新版本也没有帮助。

refer转到此图像的表设计。用于创建树的模板如下:

<li ng-repeat="item in ::item.childLevelDetails" > 
<div > 
    <a ng-click="toggle(this)" class="icon icon-stream-add-2"> 
     <span></span> 
    </a> 
    <div class="unitTextDiv">{{::item.title}}</div> 
</div> 
<ol ng-include= "'tree_node'"> 
</li> 

请求您对此问题提出任何可能的解决方案。

回答

0

你可以试试这个递归的示例中,我为你所做的。 使用ng-if显示/隐藏元素将减少手表的数量。

这里找到Fiddler

var myApp = angular.module('myApp',[]); 
 

 
myApp.controller('MyCtrl', ['$scope','$timeout', 'getWatchCount' , function ($scope ,$timeout, getWatchCount){ 
 

 
$scope.tree = [ 
 
\t {title:'Element level 1', 
 
    elements: [ 
 
     \t { title: 'Element level 1.1'}, 
 
     { title: 'Element level 1.2', 
 
      elements: [ 
 
\t \t \t { title: 'Element level 1.2.2'}, 
 
\t \t \t { title: 'Element level 1.2.2'}, 
 
      ]} 
 
    ]}, 
 
    {title:'Element level 2'} 
 
    \t ] 
 
    
 
    
 
//NEXT CODE ONLY USED FOR COUNTING WATCHES// 
 
$scope.countWatches = function(){ 
 
    \t $scope.numberOfWatches = getWatchCount(); 
 
} 
 
    
 
$timeout(function(){$scope.countWatches()} , 0); 
 
    
 
     
 
// I return the count of watchers on the current page. 
 
function getWatchCount() { 
 

 
    // Keep track of the total number of watch bindings on the page. 
 
    var total = 0; 
 

 
    // There are cases in which two different ng-scope markers will actually be referencing 
 
    // the same scope, such as with transclusion into an existing scope (ie, cloning a node 
 
    // and then linking it with an existing scope, not a new one). As such, we need to make 
 
    // sure that we don't double-count scopes. 
 
    var scopeIds = {}; 
 

 
    // AngularJS denotes new scopes in the HTML markup by appending the classes "ng-scope" 
 
    // and "ng-isolate-scope" to appropriate elements. As such, rather than attempting to 
 
    // navigate the hierarchical Scope tree, we can simply query the DOM for the individual 
 
    // scopes. Then, we can pluck the watcher-count from each scope. 
 
    // -- 
 
    // NOTE: Ordinarily, it would be a HUGE SIN for an AngularJS service to access the DOM 
 
    // (Document Object Model). But, in this case, we're not really building a true AngularJS 
 
    // service, so we can break the rules a bit. 
 
    angular.forEach(
 
     document.querySelectorAll(".ng-scope , .ng-isolate-scope"), 
 
     countWatchersInNode 
 
    ); 
 

 
    return(total); 
 

 

 
    // --- 
 
    // PRIVATE METHODS. 
 
    // --- 
 

 

 
    // I count the $watchers in to the scopes (regular and isolate) associated with the given 
 
    // element node, and add the count to the running total. 
 
    function countWatchersInNode(node) { 
 

 
     // Get the current, wrapped element. 
 
     var element = angular.element(node); 
 

 
     // It seems that in earlier versions of AngularJS, the separation between the regular 
 
     // scope and the isolate scope where not as strong. The element was flagged as having 
 
     // an isolate scope (using the ng-isolate-scope class); but, there was no .isolateScope() 
 
     // method before AngularJS 1.2. As such, in earlier versions of AngularJS, we have to 
 
     // fall back to using the .scope() method for both regular and isolate scopes. 
 
     if (element.hasClass("ng-isolate-scope") && element.isolateScope) { 
 

 
      countWatchersInScope(element.isolateScope()); 
 

 
     } 
 

 
     // This class denotes a non-isolate scope in later versions of AngularJS; but, 
 
     // possibly an isolate-scope in earlier versions of AngularJS (1.0.8). 
 
     if (element.hasClass("ng-scope")) { 
 

 
      countWatchersInScope(element.scope()); 
 

 
     } 
 

 
    } 
 

 

 
    // I count the $$watchers in the given scope and add the count to the running total. 
 
    function countWatchersInScope(scope) { 
 

 
     // Make sure we're not double-counting this scope. 
 
     if (scopeIds.hasOwnProperty(scope.$id)) { 
 

 
      return; 
 

 
     } 
 

 
     scopeIds[ scope.$id ] = true; 
 

 
     // The $$watchers value starts out as NULL until the first watcher is bound. As such, 
 
     // the $$watchers collection may not exist yet on this scope. 
 
     if (scope.$$watchers) { 
 

 
      total += scope.$$watchers.length; 
 

 
     } 
 

 
    } 
 

 
} 
 
    
 
}]); 
 

 
myApp.factory(
 
\t \t \t "getWatchCount", 
 
\t \t \t function() { 
 

 
\t \t \t \t // I return the count of watchers on the current page. 
 
\t \t \t \t function getWatchCount() { 
 

 
\t \t \t \t \t var total = 0; 
 

 
\t \t \t \t \t // AngularJS denotes new scopes in the HTML markup by appending the 
 
\t \t \t \t \t // class "ng-scope" to appropriate elements. As such, rather than 
 
\t \t \t \t \t // attempting to navigate the hierarchical Scope tree, we can simply 
 
\t \t \t \t \t // query the DOM for the individual scopes. Then, we can pluck the 
 
\t \t \t \t \t // watcher-count from each scope. 
 
\t \t \t \t \t // -- 
 
\t \t \t \t \t // NOTE: Ordinarily, it would be a HUGE SIN for an AngularJS service 
 
\t \t \t \t \t // to access the DOM (Document Object Model). But, in this case, 
 
\t \t \t \t \t // we're not really building a true AngularJS service, so we can 
 
\t \t \t \t \t // break the rules a bit. 
 
\t \t \t \t \t angular.element(".ng-scope").each(
 
\t \t \t \t \t \t function ngScopeIterator() { 
 

 
\t \t \t \t \t \t \t // Get the scope associated with this element node. 
 
\t \t \t \t \t \t \t var scope = $(this).scope(); 
 

 
\t \t \t \t \t \t \t // The $$watchers value starts out as NULL. 
 
\t \t \t \t \t \t \t total += scope.$$watchers 
 
\t \t \t \t \t \t \t \t ? scope.$$watchers.length 
 
\t \t \t \t \t \t \t \t : 0 
 
\t \t \t \t \t \t \t ; 
 

 
\t \t \t \t \t \t } 
 
\t \t \t \t \t); 
 
\t \t \t \t \t 
 
\t \t \t \t \t return(total); 
 

 
\t \t \t \t } 
 

 
\t \t \t \t // For convenience, let's serialize the above method and convert it to 
 
\t \t \t \t // a bookmarklet that can easily be run on ANY AngularJS page. 
 
\t \t \t \t getWatchCount.bookmarklet = ( 
 
\t \t \t \t \t "javascript:alert('Watchers:'+(" + 
 
\t \t \t \t \t getWatchCount.toString() 
 
\t \t \t \t \t \t .replace(/\/\/.*/g, " ") 
 
\t \t \t \t \t \t .replace(/\s+/g, " ") + 
 
\t \t \t \t \t ")());void(0);" 
 
\t \t \t \t); 
 

 
\t \t \t \t return(getWatchCount); 
 

 
\t \t \t } 
 
\t \t);
ul{ 
 
    list-style-type: none; 
 
} 
 

 
li{ 
 
    font-size:13px; 
 
} 
 
.arrow{ 
 
    width: 0; 
 
    height: 0; 
 
    border-top: 7px solid transparent; 
 
    border-bottom: 7px solid transparent; 
 
    border-right: 7px solid transparent; 
 
    cursor: pointer; 
 
    margin-left: 5px; 
 
    border-left: 7px solid #000; 
 
    display: inline-block; 
 
    transition:all 0.3s; 
 
} 
 

 
.arrow.expand { 
 
    transform: rotate(45deg); 
 
    transform-origin: 20% 50%; 
 
    margin-top: 0; 
 
} 
 

 
.arrow.none { 
 
    border-left: 7px solid #ccc; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> 
 
<div ng-app="myApp" ng-controller="MyCtrl" > 
 

 
    <p> 
 
\t \t <strong>Watch Count:</strong> {{ numberOfWatches }} 
 
    </p> 
 
    
 
    <script type="text/ng-template" id="elementTree"> 
 
     <li> 
 
      <div class="arrow" 
 
        
 
       ng-class="{expand:element.isOpen,none:!element.elements}" 
 
       ng-click="$apply(element.isOpen = !element.isOpen) ; countWatches()"> 
 
      </div> 
 
      {{element.title}} 
 
     </li> 
 
     <div ng-if="element.isOpen"> 
 
      <ul 
 
       ng-repeat="element in element.elements" 
 
       ng-include="'elementTree'"> 
 
      </ul 
 
     </div> 
 
     
 
    </script> 
 
    
 
    <ul ng-repeat="element in tree" 
 
     ng-include="'elementTree'"> 
 
    </ul> 
 
    
 
    
 
</div>

+0

通过使用angular的一次性绑定功能,手表数量从来都不是问题。主要问题是元素渲染的延迟。这里是你的[Fiddler]的分支(http://jsfiddle.net/93agvdoj/),我修改了它展示了渲染问题 –