Подтвердить что ты не робот

Способы улучшения работы AngularJS даже при большом количестве элементов DOM

Я оцениваю, следует ли использовать AngularJS для веб-проекта, и я беспокоюсь о производительности для функции, которую мне нужно реализовать. Я хотел бы знать, есть ли лучший способ реализовать функциональность, которую я пытаюсь использовать в AngularJS.

По сути, мне кажется, что время, которое AngularJS реагирует на событие, зависит от количества элементов DOM на странице, даже если элементы DOM не меняются активно и т.д. Я предполагаю, что это потому что функция $digest пересекает всю DOM.. по крайней мере из моих экспериментов, похоже, что это так.

Здесь сценарий игры (это не совсем то, что я действительно пытаюсь сделать, но достаточно близко для тестирования).

Я хотел бы, чтобы angularJS выделял слово, когда я наводил на него курсор. Однако по мере увеличения количества слов на странице происходит большая задержка между тем, когда вы наводите курсор на слово и когда оно действительно выделено.

jsfiddle, который показывает это: http://jsfiddle.net/czerwin/5qFzg/4/

(Кредит: этот код основан на сообщении от Питера Бэкона Дарвина на форуме AngularJS).

Здесь HTML:

<div ng-app="myApp">
    <div ng-controller="ControllerA">
        <div >
            <span ng-repeat="i in list" id="{{i}}" ng-mouseover='onMouseover(i)'>
                {{i}}, 
            </span>
            <span ng-repeat="i in listB">
                {{i}}, 
            </span>
        </div>
    </div>
</div>

Здесь javascript:

angular.module('myApp', [])
.controller('ControllerA', function($scope) {
    var i;
    $scope.list = [];
    for (i = 0; i < 500; i++) {
        $scope.list.push(i);
    }

    $scope.listB = [];
    for (i = 500; i < 10000; i++) {
        $scope.listB.push(i);
    }

    $scope.highlightedItem = 0;
    $scope.onMouseover = function(i) {
        $scope.highlightedItem = i;
    };

    $scope.$watch('highlightedItem', function(n, o) {
        $("#" + o).removeClass("highlight");
        $("#" + n).addClass("highlight");
    });
});

Примечания: - Да, я использую jquery для манипуляции с DOM. Я пошел по этому маршруту, потому что это был способ зарегистрировать одного наблюдателя. Если я делаю это исключительно в angularJS, мне придется регистрировать обработчик наведения на каждый диапазон, и это, как представляется, делает страницу медленной. - Я реализовал этот подход и в чистом jquery, и производительность была прекрасной. Я не верю, что вызовы jquery меня замедляют. - Я сделал только первые 500 слов, чтобы иметь идентификаторы и классы, чтобы убедиться, что на самом деле просто есть больше элементов DOM, которые, кажется, замедляют их (вместо элементов DOM, на которые может повлиять операция).

4b9b3361

Ответ 1

Я думаю, что лучший способ решить проблемы производительности - избегать использования абстракций высокого уровня (AngularJS ng-repeat со всей соответствующей магией фона) в таких ситуациях. AngularJS не является серебряной пулей и отлично работает с низкоуровневыми библиотеками. Если вам нравится такая функциональность в текстовом блоке, вы можете создать директиву, которая будет контейнером для текста и инкапсулировать всю логику низкого уровня. Пример с пользовательской директивой, в которой используется плагин jingery letteringjs:

angular.module('myApp', [])
  .directive('highlightZone', function () {
    return {
      restrict: 'C',
      transclude: true,
      template: '<div ng-transclude></div>',
      link: function (scope, element) {
        $(element).lettering('words')
      }
    }
  })

http://jsfiddle.net/j6DkW/1/

Ответ 2

Хотя принятый ответ существует уже давно, я думаю, его важно понять, почему angularJS так медленно реагирует на код, который вы указали. На самом деле angularJS не медленно с множеством элементов DOM, в этом случае он медленный из-за директивы ng-mouseover, которую вы регистрируете по каждому элементу в своем списке. Директива ng-mouseover регистрирует прослушиватель событий onmouseover, и каждый раз, когда запускается функция слушателя, выполняется ng. $Apply(), которая запускает грязную проверку сравнения $diggest и просматривает все часы и привязки.

Короче говоря: каждый раз, когда элемент становится зависающим, вы можете потреблять, например. 1-6 мс для внутренних
angular грязная проверка сравнения (в зависимости от количества привязок, которые вы установили). Не хорошо:)

Это связанная с этим реализация с угловым исполнением:

var ngEventDirectives = {};
    forEach('click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
        function(name) {
            var directiveName = directiveNormalize('ng-' + name);
            ngEventDirectives[directiveName] = ['$parse', function($parse) {
            return {
                compile: function($element, attr) {
                    var fn = $parse(attr[directiveName]);
                    return function(scope, element, attr) {
                        element.on(lowercase(name), function(event) {
                            scope.$apply(function() {
                                fn(scope, {$event:event});
                            });
                        });
                    };
                }
            };
       }];
    }
);

Фактически, для выделения парированного текста вы, вероятно, просто используете CSS:

.list-item:hover {
    background-color: yellow;
}

Вероятно, что с более новыми Angular версиями ваш код как есть, будет работать значительно быстрее. Для Angular версии 1.3 существует оператор bind-once:: который исключает односвязные переменные из цикла дайджеста. Наличие тысяч предметов, исключенных, значительно уменьшит нагрузку на дайджест.

Как и в случае ECMAScript 6, Angular может использовать класс Observe, который сделает проверку грязного сравнения полностью устаревшей. Таким образом, одно наведение мыши будет выводиться внутри одного обратного вызова по событию, больше не будет применяться или не будет загружаться. Все с исходным кодом. Когда Angular применит это, я не знаю. Думаю, в 2.0.

Ответ 3

Это старый вопрос сейчас, но я думаю, что стоит добавить в микс, что Angular (начиная с версии v1.3) теперь поддерживает одно привязку времени, которая помогает сгладить цикл дайджеста. Я работал над несколькими приложениями, в которых добавление однократной привязки значительно сократило количество часов, что привело к повышению производительности. ng-repeat часто несет ответственность за добавление большого количества часов, поэтому вы можете подумать о добавлении однократной привязки к ng-repeat.

ng-repeat="i in ::list"

Вот краткое изложение нескольких методов, которые можно использовать, чтобы избежать добавления ненужных часов

http://www.syntaxsuccess.com/viewarticle/547a8ba2c26c307c614c715e

Ответ 4

Всегда сначала просматривайте профиль, чтобы найти реальное узкое место. Иногда это может быть не то, что вы изначально подозреваете. Сначала я подозреваю ваш собственный код, а затем Angular (большое количество наблюдателей является главной особенностью, ведущей к медленной производительности). Я описал, как профилировать и решать различные проблемы с производительностью в приложении Angular в подробном сообщении в блоге https://glebbahmutov.com/blog/improving-angular-web-app-performance-example/

Ответ 5

Получите zone.js из btford и запустите все функции в зоне, чтобы проверить свое время, затем создайте зоны для обработки ajax-кода (crud) и другие зоны для статического кода (apis).

В качестве альтернативы ограничение ng-повтора и/или отключение двусторонней привязки к объектам имеет большое значение... проблема, которую веб-компоненты уже покрывают, используя теневое хранилище, оставляя верхний хрустящий на ощупь. still zone.js - просмотрите видео по ссылке на правдоподобности.

Ответ 6

Хорошо, я вижу, что вы используете $watch. Angular рекоммендует $watch, когда это очень необходимо. Сценарии, такие как обновление переменной через ng-model

http://jsfiddle.net/5qFzg/10/

Сураджа