ng-style 的坑 - 對性能的影響


 

本文地址:http://www.cnblogs.com/jying/p/5633203.html 

 

熟悉 angular 的前端對ng-style 一定不陌生,這個家伙可以綁定一個函數,使得我們可以在函數中根據不同的參數返回不同的樣式,如下是一個簡單的實例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body onselectstart='return false'>
    <div ng-app="myApp" ng-controller="myCtrl" style="overflow-wrap:break-word;">
        <span ng-repeat="idx in data" ng-style="setStyle(idx)">
            {{idx+','}}
        </span>
    </div>

    <script type="text/javascript">
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function($scope) {
            $scope.data = [];
            for(var i=1;i<=10000;i++){
                $scope.data.push(i);
            }

            $scope.setStyle = function(idx){
                switch(idx%4){
                    case 1:return {"color":"red"};
                    case 2: return {"color":"chartreuse"};
                    case 3:return {"color":"yellow"};
                    case 0:return {"color":"blue"};
                    default : return {};
                }
            }

        });
    </script>
</body>
</html>
ng-style 綁定文字顏色

在該實例中,我們通過 $index 綁定不同的文字顏色,是不是感覺很方便呢,程序猿和代碼其樂融融,相處的很好嘛O(∩_∩)O

 

直到有一天......業務提出這么一個需求:在成千上萬的 span 上拖動鼠標選擇區域設置背景色!

拖動鼠標嘛,簡單!我們有 ng-mouseenter、ng-mouseleave、ng-mousemove、ng-mouseup 、ng-mousedown!挽起袖子搞起! 

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body onselectstart='return false' style="-moz-user-select:none;">
    <div ng-app="myApp" ng-controller="myCtrl" style="overflow-wrap:break-word;">
        <span ng-repeat="idx in data" ng-style="setStyle(idx)" 
            ng-mousedown="mousedown($event)" ng-mouseup="mouseup($event)" ng-mouseenter="mouseenter($event)">
            {{idx+','}}
        </span>
    </div>

    <script type="text/javascript">
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function($scope) {
            $scope.data = [];
            for(var i=1;i<=10000;i++){
                $scope.data.push(i);
            }

            $scope.setStyle = function(idx){
                switch(idx%4){
                    case 1:return {"color":"red"};
                    case 2: return {"color":"chartreuse"};
                    case 3:return {"color":"yellow"};
                    case 0:return {"color":"blue"};
                    default : return {};
                }
            }

            //識別鼠標是否按下
            $scope.isdown = false;
            //鼠標按下
            $scope.mousedown = function(e){
                $scope.isdown = true;
            }
            //鼠標抬起
            $scope.mouseup = function(e){
                $scope.isdown = false;
            }
            //鼠標進入
            $scope.mouseenter = function(e){
                if($scope.isdown){
                    console.log(e.target.style.backgroundColor = "#eeeeee");
                }
            }
        });
    </script>
</body>
</html>
ng-mouse 事件拖動鼠標綁定背景色

好像也沒有什么問題嘛 →.→ 其實業務給的需求場景比這個復雜的多,這里只是舉例說明所以目前沒有感覺出現問題,那么ng-style 是在什么時候綁定控件的 style 樣式呢?於是給$scope.setStyle 添加輸出:console.log(idx);  結果剛才較流暢的界面卡出翔了 ←.← 

好吧,我們把數字改小點看看效果 ↓ . ↓

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body onselectstart='return false' style="-moz-user-select:none;">
    <div ng-app="myApp" ng-controller="myCtrl" style="overflow-wrap:break-word;background-color: #dddddd;">
        <span ng-repeat="idx in data" ng-style="setStyle(idx)" 
            ng-mousedown="mousedown($event)" ng-mouseup="mouseup($event)" ng-mouseenter="mouseenter($event)">
            {{idx+','}}
        </span>
    </div>

    <script type="text/javascript">
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function($scope) {
            $scope.data = [];
            for(var i=1;i<=100;i++){
                $scope.data.push(i);
            }

            $scope.setStyle = function(idx){
                console.log(idx + " -- "+ (new Date()).getMilliseconds());
                switch(idx%4){
                    case 1:return {"color":"red"};
                    case 2: return {"color":"chartreuse"};
                    case 3:return {"color":"yellow"};
                    case 0:return {"color":"blue"};
                    default : return {};
                }
            }

            //識別鼠標是否按下
            $scope.isdown = false;
            //鼠標按下
            $scope.mousedown = function(e){
                $scope.isdown = true;
            }
            //鼠標抬起
            $scope.mouseup = function(e){
                $scope.isdown = false;
            }
            //鼠標進入
            $scope.mouseenter = function(e){
                if($scope.isdown){
                    console.log(e.target.style.backgroundColor = "red");
                }
            }
        });
    </script>
</body>
</html>
ng-style 函數中寫入console.log

按下 F12 查看控制台 ,鼠標移入數字區,可以看到console.log 不停的輸出,也就是說此時 ng-style 是不停的重復綁定的,這顯然是耗費資源的,在某些實時要求高的界面就會導致卡頓現象,為了避免這種重復的資料消耗決定換個方式綁定style,且要只綁定一次,想來想去決定還是用 for 遍歷控件綁定

            $scope.data = [];
            for(var i=1;i<=100;i++){
                $scope.data.push(i);
            }
            var spans = document.querySelectorAll("span");
            for(var j=0;j<spans.length;j++){
                var span = spans[j];
                console.log(span);
            }

然而這樣獲取到 spans 為[] ,因為此時ng-repeat 還沒有渲染完 span ,根據js單線程原理,此時應該用$timeout(function(){},0); 原理請閱讀:setTimeout 的黑魔法 

看來以后要棄用 ng-style 的使用了。

全部實現代碼如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body onselectstart='return false' style="-moz-user-select:none;">
    <div ng-app="myApp" ng-controller="myCtrl" style="overflow-wrap:break-word;background-color: #dddddd;">
        <!--<span ng-repeat="idx in data" ng-style="setStyle(idx)" 
            ng-mousedown="mousedown($event)" ng-mouseup="mouseup($event)" ng-mouseenter="mouseenter($event)">
            {{idx+','}}
        </span>-->

        <span ng-repeat="idx in data" test-index ={{idx}}
            ng-mousedown="mousedown($event)" ng-mouseup="mouseup($event)" ng-mouseenter="mouseenter($event)">
            {{idx+','}}
        </span>
    </div>

    <script type="text/javascript">
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function($scope,$timeout) {
            $scope.data = [];
            for(var i=1;i<=100;i++){
                $scope.data.push(i);
            }
            $timeout(function(){
                var spans = document.querySelectorAll("span");
                for(var j=0;j<spans.length;j++){
                    var span = spans[j];
                    console.log(span.style.color =$scope.setStyle(span.attributes["test-index"].value));
                }
            },0);

            $scope.setStyle = function(idx){
                console.log(idx + " -- "+ (new Date()).getMilliseconds());
                switch(idx%4){
                    case 1:return "red";
                    case 2: return "chartreuse";
                    case 3:return "yellow";
                    case 0:return "blue";
                    default : return "#fff";
                }
            }

            //識別鼠標是否按下
            $scope.isdown = false;
            //鼠標按下
            $scope.mousedown = function(e){
                $scope.isdown = true;
            }
            //鼠標抬起
            $scope.mouseup = function(e){
                $scope.isdown = false;
            }
            //鼠標進入
            $scope.mouseenter = function(e){
                if($scope.isdown){
                    console.log(e.target.style.backgroundColor = "red");
                }
            }
        });
    </script>
</body>
</html>
棄用 ng-style

 

 

個人小站歡迎來踩:駕校教練評價平台 | 為愛豆砌照片牆

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM