23

我正在评估是否将 AngularJS 用于 Web 项目,并且我担心我需要实现的功能的性能。我想知道是否有更好的方法来实现我在 AngularJS 中尝试的功能。

从本质上讲,在我看来,AngularJS 对事件做出反应的时间取决于页面中 DOM 元素的数量,即使 DOM 元素没有被主动更改等等。我猜这是因为$digest 函数正在遍历整个 DOM .. 至少从我的实验来看,似乎是这样。

这是游戏场景(这并不是我真正想要做的,但足够接近用于测试目的)。

当我将鼠标悬停在它上面时,我想让 angularJS 突出显示一个单词。但是,随着页面中单词数量的增加,将鼠标悬停在单词上与实际突出显示时之间会有更大的延迟。

显示这个的 jsfiddle:http: //jsfiddle.net/czerwin/5qFzg/4/

(信用:此代码基于 Peter Bacon Darwin 在 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 个单词中添加了 id 和 classes,以验证它实际上只是有更多的 DOM 元素似乎会减慢它们的速度(而不是可能受操作影响的 DOM 元素)。

4

6 回答 6

13

尽管已经存在公认的答案,但我认为了解为什么 angularJS 对您提供的代码反应如此缓慢很重要。实际上 angularJS 对于很多 DOM 元素并不慢,在这种情况下它很慢,因为您在列表中的每个项目上注册了 ng-mouseover 指令。ng-mouseover 指令注册了一个 onmouseover 事件监听器,并且每次监听器函数被触发时,都会执行一个 ng.$apply() 来运行 $diggest 脏比较检查并遍历所有监视和绑定。

简而言之:每次元素悬停时,您可能会花费 1-6 毫秒进行内部
角度脏比较检查(取决于您已建立的绑定计数)。不好 :)

这就是相关的angularJS实现:

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 版,有绑定一次运算符 :: 它将从摘要循环中排除一次绑定的变量。排除数千个项目将显着减少摘要负载。

与 ECMAScript 6 一样,angular 可以使用 Observe 类,这将使脏比较检查完全过时。因此,单个鼠标悬停将在内部导致单个事件回调,不再应用或挖掘。全部使用原始代码。Angular 什么时候会应用这个,我不知道。我猜在2.0。

于 2013-12-13T13:00:35.410 回答
10

我认为解决性能问题的最佳方法是避免在这种情况下使用高级抽象(AngularJS ng-repeat 和所有相应的背景魔法)。AngularJS 不是灵丹妙药,它与低级库完美配合。如果您喜欢文本块中的此类功能,您可以创建一个指令,它将作为文本容器并封装所有低级逻辑。带有自定义指令的示例,它使用 letteringjs jquery 插件:

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/

于 2013-04-21T06:01:28.910 回答
8

现在这是一个老问题了,但我认为值得一提的是 Angular(从 v1.3 开始)现在支持一次性绑定,这有助于减少摘要循环。我在一些应用程序中工作过,其中添加一次绑定显着减少了手表的数量,从而提高了性能。ng-repeat 通常负责添加很多手表,因此您可能会考虑向 ng-repeat 添加一次绑定。

ng-repeat="i in ::list"

这里总结了一些可以用来避免添加不必要的手表的技巧

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

于 2014-11-30T07:30:28.677 回答
4

始终首先分析以找到真正的瓶颈。有时它可能不是您最初怀疑的。我会首先怀疑您自己的代码,然后是 Angular(大量观察者是导致性能低下的主要特征)。我在详细的博客文章https://glebbahmutov.com/blog/improving-angular-web-app-performance-example/中描述了如何在 Angular 应用程序中分析和解决不同的性能问题

于 2014-11-14T05:51:27.223 回答
0

从 btford获取zone.js并运行区域中的所有函数以检查它们的时间,然后创建区域来处理 ajax 代码 (crud) 和其他区域用于静态代码 (apis)。

或者,限制 ng-repeat 和/或禁用对象上的双向绑定会大有帮助。Web 组件已经通过使用 shadow dom 解决了这个问题,让顶部变得酥脆。仍然 zone.js - 通过关于似然性的链接观看视频。

于 2014-11-14T07:01:55.327 回答
-1

好吧,我可以看到你正在使用$watch. Angular 在非常需要时推荐 $watch 。像通过以下方式更新变量的场景ng-model

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

苏拉杰

于 2013-08-30T06:28:53.537 回答