ng-repeat中DOM的重新渲染机制

引子:项目中遇到一个BUG,使用ui-bootstrap-tpls中的carousel插件设置轮播图时,改变ng-repeat中数组其中一个元素时,轮播图的顺序发生了变化。
            代码如下: 

<div carousel interval="app.Interval.value" no-pasue="false" no-transition="true">
    <div slide ng-repeat="Item in advertiseList">
        <div ng-style="{'background-color': Item.BgColor}">
            <div class="slide">
                <img class="aside cursor-pointer" ng-src="{{Item.ImgUrl|ImgUrl}}">
            </div>
        </div>
    </div>
</div>
function changeAd() {
        angular.forEach($scope.advertiselList, function (item, index) {

        if (item.Id == $scope.editAd.Id) {
           $scope.advertiseList[index] = $scope.editAd;
        }
    });
}
通过查看carsousel与slide组合指令的源码发现,当slide的DOM被重新渲染时,slide指令会在其销毁时先将其移除,然后渲染时push到尾部。
在不想改变指令的情况下,这个问题只能从阻止ng-repeat对DOM的重新渲染着手。
正文:关于ng-repeat的重新渲染机制

如果ng-repeat数组的每个item都是一个对象,ng-repeat会为每个item都生成一个$$hashKey,用于绑定DOM。


情况一:通过track by传入标识属性,那么ng-repeat会把此标识属性绑定到DOM,并无视掉$$hashKey
解决方案:因此,这个问题可以简单的通过将ng-repeat="Item in advertiseList"改变为ng-repeat="Item in advertiseList track by $index(track by item.Id)"来解决。

在这种语法下,$$hashKey不起作用的,只要item.Id相等就会认为两个对象相同。


情况二:当没有通过track by传入标识属性的情况下,默认是通过$$hashKey来绑定DOM。而$$hashKey的改变与否,是通过$watchCollection对数组进行监听,当监听到有变化时,会改变$$hashKey的值并重新渲染DOM。
这里补充一下$watchCollection与$watch(obj,listener)、$watch(obj,listner,true)的区别
$watch(obj,listener)为引用比对,即只比对对象或数组的引用,对象中单个属性变化或者数组中元素的变化并不会触发listener函数,如果将对象或数组想象成树,那么$watch为只比对根节点。
$watch(obj,listner,true)为值比对,$watch的第三个入参含义为是否值比对(或者全比对更好理解一点)即比对整个对象或数组所有的值,如果数组的元素是对象,会继续比对对象元素的所有属性,以此类推。可以理解成对象树的全遍历。
第一种方案策略太过简单,第二种策略又太消耗性能,所以诞生了一个折中的浅层监听策略。
$watchCollection是基于$watch监听的浅层监听函数,即对于数组来说,监听每个元素的引用,对于对象来说,监听每个属性的引用。可以理解为只遍历树的根节点和第一层元素。
解决方案:针对浅层监听的机制,我们可以通过不改变数组中item引用的方法来骗过监听。
$scope.advertiseList[index] = $scope.editAd;
前面这句代码只是简单的将$scope.advertiselList[index]的引用指向了一个新的地址,因此可以被检测到,可以将这句话改成
angular.copy($scope.editAd,$scope.advertiseList)
angular中的copy入参传入目标对象,会将源对象的所有属性一一赋值给目标对象,不会改变目标对象的引用。(详情请看另一篇angular中的深浅拷贝,如果“链接”点不动,说明还没写)。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值