(十三)通过DOM事件DOMNodeRemoved,看ng-repeat的性能问题以及track by的作用
1.DOMNodeRemoved事件
DOMNodeRemoved事件这里不做过多介绍,有个大概的认识,会使用即可。如下代码给content对象注册了DOMNodeRemoved事件处理函数,当content下有子元素被删除的时候,就会触发DOMNodeRemoved事件。
<script> window.onload = function(){ var dom = document.getElementById("content"); dom.addEventListener("DOMNodeRemoved",function(event){ }); }; </script> <body> <div id="content"> <div></div> <!--commet--> <span></span> </div> </body>
2.ng-repeat遍历的数组为空或者长度为0的时候
当ng-repeat遍历的数组为空或者长度为0的时候,会生成一个HTML注释。
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>ng-repeat</title> <script src="jquery-1.11.1.js"></script> <script src="angular-1.2.25.js"></script> <script> function wholeController($scope,$rootScope,$injector) { $scope.dataList = null; //$scope.dataList = []; } </script> </head> <body ng-app> <div ng-controller="wholeController"> <div>begin</div> <div id="content"> <div ng-repeat="item in dataList">{{item.name}}</div> </div> <div>end</div> </div> </body> </html>ng-repeat生成的dom元素如下,就是一段html注释。
<div id="content"> <!-- ngRepeat: item in dataList --> </div>
3.ng-repeat中不使用track by子语句
由于angularjs数据的双向绑定特性,当scope中的数据发生改变的时候,会自动刷新界面。
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>ng-repeat</title> <script src="jquery-1.11.1.js"></script> <script src="angular-1.2.25.js"></script> <script> $(function(){ var domObject = $("#content").get(0); domObject.addEventListener("DOMNodeRemoved",function(event){ console.log("some dom was deleted."); }); }); function wholeController($scope,$rootScope,$injector) { $scope.dataList = [ {"id":1,"name":"a1"}, {"id":2,"name":"a2"}, {"id":3,"name":"a3"}, {"id":4,"name":"a4"} ]; $scope.first = function(){ $scope.dataList = [ {"id":1,"name":"b1"}, {"id":2,"name":"b2"}, {"id":3,"name":"b3"}, {"id":4,"name":"b4"} ]; } } </script> </head> <body ng-app> <div ng-controller="wholeController"> <input type="button" value="first" ng-click="first();"/> <div>begin</div> <div id="content"> <div ng-repeat="item in dataList">{{item.name}}</div> </div> <div>end</div> </div> </body> </html>当点击first按钮的时候,scope中的dataList数据发生了变化,界面会自动刷新。如果要实现dom的刷新有2种方式:
方式一:删除之前所有存在的dom,然后重新生成dom。
方式二:重用之前的dom元素,仅仅更新dom元素的属性。
在没有使用track by的情况下,angular采用的是方式一,这一点可以通过我们注册的DOMNodeRemoved事件处理函数得到证实。我们知道dom的频繁操作是非常耗费性能的,那为什么 ng-repeat 不能利用已有的 dom 元素去更新数据呢?因为你没有把数组元素的标识属性告诉它,ng-repeat不知道怎么替换。在没有使用track by的情况下,我们可以看到 ng-repeat 往数组里每个元素加了一个 $$hashKey 的属性:
这个 key 是由 Angular 内部的 nextUid() 方法生成,类似数据库自增,但是是使用字符串。现在我们明白了,因为每次替换数组都会导致 ng-repeat 为每个元素生成一个新 key, 所以根本没办法重用已有的 Dom 元素。那怎么解决这个问题呢?就是使用track by子语句。将ng-repeat改成下面的方式1或者方式2,就可以发现没有dom删除事件。
<!--方式1--> <div ng-repeat="item in dataList track by item.id">{{item.name}}</div> <!--方式2--> <div ng-repeat="item in dataList track by $index">{{item.name}}</div>
4.数组长度未变,但是顺序发生了变化
上面的代码中,点击first按钮,dataList只是name属性值发生了改变,数组长度和顺序都没有发生变化。
[ {"id":1,"name":"a1"}, {"id":2,"name":"a2"}, {"id":3,"name":"a3"}, {"id":4,"name":"a4"} ]; 变成 [ {"id":1,"name":"b1"}, {"id":2,"name":"b2"}, {"id":3,"name":"b3"}, {"id":4,"name":"b4"} ];
如果我们改变dataList中元素的顺序呢?
[ {"id":1,"name":"a1"}, {"id":2,"name":"a2"}, {"id":3,"name":"a3"}, {"id":4,"name":"a4"} ]; 变成 [ {"id":4,"name":"b1"}, {"id":2,"name":"b2"}, {"id":3,"name":"b3"}, {"id":1,"name":"b4"} ];
下面这段代码中,我们改变dataList的顺序,然后使用track by $index和track by item.id看看是什么效果。
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>ng-repeat</title> <script src="jquery-1.11.1.js"></script> <script src="angular-1.2.25.js"></script> <script> $(function(){ var domObject = $("#content").get(0); domObject.addEventListener("DOMNodeRemoved",function(event){ console.log("some dom was deleted."+event.target); }); }); function wholeController($scope,$rootScope,$injector) { $scope.dataList = [ {"id":1,"name":"a1"}, {"id":2,"name":"a2"}, {"id":3,"name":"a3"}, {"id":4,"name":"a4"} ]; $scope.first = function(){ $scope.dataList = [ {"id":4,"name":"b1"}, {"id":2,"name":"b2"}, {"id":3,"name":"b3"}, {"id":1,"name":"b4"} ]; } } </script> </head> <body ng-app> <div ng-controller="wholeController"> <input type="button" value="first" ng-click="first();"/> <div>begin</div> <div id="content"> <div ng-repeat="item in dataList track by item.id">{{item.name}}</div> <!-- <div ng-repeat="item in dataList track by $index">{{item.name}}</div> --> </div> <div>end</div> </div> </body> </html>
可以看到使用track by $index的时候,不会发生dom删除事件,即是更新dom元素, 而不是先删除再新建。当使用track by item.id的时候,会发生dom删除事件。也就是说,当数组中元素顺序改变的时候,使用track by item.id与不使用track by没有什么差别。大家可以试试看当数组的长度发生变化时,ng-repeat的表现是什么样子的。
http://www.cnblogs.com/MigCoder/p/3930264.html
文章来自:http://blog.csdn.net/aitangyong/article/details/44106921