I'm evaluating whether or not to use AngularJS for a web project, and I'm worried about the performance for a feature I need to implement. I would like to know if there's a better way to implement the functionality I'm trying to in AngularJS.
我正在评估是否要在web项目中使用AngularJS,我担心需要实现的特性的性能。我想知道是否有更好的方法来实现我在AngularJS中尝试的功能。
Essentially, it seems to me the time it takes AngularJS to react to an event is dependent on the number of DOM elements in the page, even when the DOM elements aren't being actively changed, etc. I'm guessing this is because the $digest function is traversing the entire DOM.. at least from my experiments, that seems to be the case.
从本质上讲,在我看来,AngularJS对事件的响应时间取决于页面中DOM元素的数量,即使DOM元素没有被积极地更改,等等。我猜这是因为$digest函数是遍历整个DOM的。至少从我的实验来看是这样。
Here's the play scenario (this isn't exactly what I'm really trying to do, but close enough for testing purposes).
这是一个游戏场景(这不是我真正想做的,但是对于测试目的来说已经足够接近了)。
I would like to have angularJS highlight a word as I hover over it. However, as the number of words in the page increases, there's a larger delay between when you hover over the word and when it is actually highlighted.
我想让angularJS突出一个词,当我悬停在它上面。但是,随着页面中的单词数量的增加,当鼠标悬停在单词上时和它实际上被高亮显示时之间存在较大的延迟。
The jsfiddle that shows this: http://jsfiddle.net/czerwin/5qFzg/4/
显示这个的jsfiddle: http://jsfiddle.net/czerwin/5qFzg/4/。
(Credit: this code is based on a post from Peter Bacon Darwin on the AngularJS forum).
(注:此代码基于Peter Bacon Darwin在AngularJS论坛上的一篇文章)。
Here's the HTML:
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>
Here's the javascript:
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");
});
});
Things to note: - Yes, I'm using jquery to do the DOM manipulation. I went this route because it was a way to register one watcher. If I do it purely in angularJS, I would have to register a mouseover handler for each span, and that seemed to make the page slow as well. - I implemented this approach in pure jquery as well, and the performance was fine. I don't believe it's the jquery calls that are slowing me down here. - I only made the first 500 words to have id's and classes to verify that it's really just having more DOM elements that seems to slow them down (instead of DOM elements that could be affected by the operation).
注意事项:-是的,我正在使用jquery进行DOM操作。我走这条路是因为这是一个注册一个观察者的方法。如果我只使用angularJS,那么我必须为每个span注册一个鼠标悬停处理程序,这似乎也会使页面变慢。-我也用纯jquery实现了这个方法,性能很好。我不相信是jquery调用让我慢了下来。-我只做了前500个有id和类的单词来验证它实际上只是有更多的DOM元素来减慢它们(而不是可能受操作影响的DOM元素)。
6 个解决方案
#1
10
I think that the best way to solve performance issues is to avoid using high level abstractions (AngularJS ng-repeat with all corresponding background magic) in such situations. AngularJS is not a silver bullet and it's perfectly working with low level libraries. If you like such functionality in a text block, you can create a directive, which will be container for text and incapsulate all low level logic. Example with custom directive, which uses letteringjs jquery plugin:
我认为解决性能问题的最佳方法是避免在这种情况下使用高级抽象(AngularJS ng-repeat with所有相应的后台魔法)。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/
#2
13
Although an accepted answer exists allready, I think its important to understand why angularJS reacts so slow at the code you provided. Actually angularJS isnt slow with lots of DOM elements, in this case it's slow because of the ng-mouseover directive you register on each item in your list. The ng-mouseover directive register an onmouseover event listener, and every time the listener function gets fired, an ng.$apply() gets executed which runs the $diggest dirty comparision check and walks over all watches and bindings.
虽然一个公认的答案已经存在,但是我认为理解为什么angularJS对您提供的代码反应如此缓慢是很重要的。实际上,angularJS对大量DOM元素并不慢,在这种情况下,它慢是因为您在列表中的每个条目上注册了ng-mouseover指令。ng-mouseover指令注册一个onmouseover事件侦听器,每次触发侦听器函数时,执行一个ng.$apply(),运行$diggest dirty comparision检查并遍历所有的监视和绑定。
In short words: each time an element gets hovered, you might consume e.g. 1-6 ms for the internal
angular dirty comparision check (depending on the count of bindings, you have established). Not good :)
简而言之:每次一个元素被悬空时,您可能要花费1-6毫秒进行内部的角脏比较检查(取决于绑定的数量,您已经建立了)。不太好:)
Thats the related angularJS implementation:
这就是相关的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});
});
});
};
}
};
}];
}
);
In fact, for highlighting a hovered text, you probably would use CSS merely:
事实上,为了突出显示一个悬空文本,您可能只会使用CSS:
.list-item:hover {
background-color: yellow;
}
It is likely that with newer Angular Versions, your code as is, will run significantly faster. For angular version 1.3 there is the bind-once operator :: which will exclude once-binded variables from the digest loop. Having thousands of items, exluded will reduce the digest load significantly.
很有可能,在更新的角度版本中,你的代码会运行得更快。对于角度版本1.3,有一个绑定-once操作符::,它将从摘要循环中排除一次性绑定的变量。有了数千个项目,exluded将大大减少消化负荷。
As with ECMAScript 6, angular can use the Observe class, which will make the dirty comparisiion check totaly obsolete. So a single mouseover would result internally in a single event callback, no more apply or diggesting. All with the original code. When Angular will apply this, I dont know. I guess in 2.0.
与ECMAScript 6一样,角可以使用观察类,这将使脏比较检查totaly过时。因此,一个鼠标悬停将在内部产生一个事件回调,不再应用或diggesting。都是原始代码。当角作用于这个,我不知道。我想在2.0。
#3
7
This is an old question now, but I think it's worth adding to the mix that Angular (since v1.3) now supports one time binding which helps slim down the digest loop. I have worked on a few applications where adding one time binding reduced the number of watches dramatically which led to improved performance. ng-repeat is often responsible for adding a lot of watches, so you could potentially consider adding one time binding to the ng-repeat.
这是一个古老的问题,但我认为值得加入这个有棱角的组合(因为v1.3)现在支持一个时间绑定,它可以帮助简化摘要循环。我曾参与过一些应用程序,在这些应用程序中,增加一次时间绑定可以显著减少手表数量,从而提高性能。ng-repeat通常负责添加大量的手表,所以您可能会考虑添加一个时间绑定到ng-repeat。
ng-repeat="i in ::list"
Here is a summary of a few techniques that can be used to avoid adding unnecessary watches
下面总结了一些可以避免添加不必要手表的技术
http://www.syntaxsuccess.com/viewarticle/547a8ba2c26c307c614c715e
http://www.syntaxsuccess.com/viewarticle/547a8ba2c26c307c614c715e
#4
4
Always profile first to find the real bottleneck. Sometimes it might be not something you initially suspect. I would suspect your own code first, then Angular (high number of watchers being the main feature leading to sluggish performance). I described how to profile and solve different performance problems in an Angular app in detailed blog post https://glebbahmutov.com/blog/improving-angular-web-app-performance-example/
总是先配置文件以找到真正的瓶颈。有时它可能不是你最初怀疑的东西。我首先会怀疑您自己的代码,然后是有棱角的(大量的观察者是导致性能迟缓的主要特性)。我在详细的博客文章https://glebbahmutov.com/blog/improing-angular -web-app-performance-example/中描述了如何在一个有棱角的应用中描述和解决不同的性能问题
#5
0
Get zone.js from btford and run all functions in a zone to check their times, then create zones to handle ajax code (crud) and other zones for static code (apis).
区。来自btford的js在一个区域运行所有的函数来检查他们的时间,然后创建区域来处理ajax代码(crud)和其他静态代码区域(api)。
Alternatively, limiting ng-repeat and/or disabling two-way binding on objects goes a long way atm.. a problem that web components already covers by using shadow dom leaving the top crispy to touch. still zone.js - watch the video through the link on plausibilities.
或者,限制ng-repeat和/或禁用对象上的双向绑定会有很长的路要走。一个web组件已经通过使用阴影dom来覆盖的问题,它让顶部变得酥脆。还带。通过貌似可信的链接观看视频。
#6
-1
Well, I can see you are using $watch
. Angular recomends $watch whenever it is very much needed. SCenarios like updating a variable through ng-model
我看得出你在用$watch。有角度的回报$watch无论何时都是非常需要的。比如通过ng-model更新变量
http://jsfiddle.net/5qFzg/10/
Suraj
苏拉
#1
10
I think that the best way to solve performance issues is to avoid using high level abstractions (AngularJS ng-repeat with all corresponding background magic) in such situations. AngularJS is not a silver bullet and it's perfectly working with low level libraries. If you like such functionality in a text block, you can create a directive, which will be container for text and incapsulate all low level logic. Example with custom directive, which uses letteringjs jquery plugin:
我认为解决性能问题的最佳方法是避免在这种情况下使用高级抽象(AngularJS ng-repeat with所有相应的后台魔法)。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/
#2
13
Although an accepted answer exists allready, I think its important to understand why angularJS reacts so slow at the code you provided. Actually angularJS isnt slow with lots of DOM elements, in this case it's slow because of the ng-mouseover directive you register on each item in your list. The ng-mouseover directive register an onmouseover event listener, and every time the listener function gets fired, an ng.$apply() gets executed which runs the $diggest dirty comparision check and walks over all watches and bindings.
虽然一个公认的答案已经存在,但是我认为理解为什么angularJS对您提供的代码反应如此缓慢是很重要的。实际上,angularJS对大量DOM元素并不慢,在这种情况下,它慢是因为您在列表中的每个条目上注册了ng-mouseover指令。ng-mouseover指令注册一个onmouseover事件侦听器,每次触发侦听器函数时,执行一个ng.$apply(),运行$diggest dirty comparision检查并遍历所有的监视和绑定。
In short words: each time an element gets hovered, you might consume e.g. 1-6 ms for the internal
angular dirty comparision check (depending on the count of bindings, you have established). Not good :)
简而言之:每次一个元素被悬空时,您可能要花费1-6毫秒进行内部的角脏比较检查(取决于绑定的数量,您已经建立了)。不太好:)
Thats the related angularJS implementation:
这就是相关的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});
});
});
};
}
};
}];
}
);
In fact, for highlighting a hovered text, you probably would use CSS merely:
事实上,为了突出显示一个悬空文本,您可能只会使用CSS:
.list-item:hover {
background-color: yellow;
}
It is likely that with newer Angular Versions, your code as is, will run significantly faster. For angular version 1.3 there is the bind-once operator :: which will exclude once-binded variables from the digest loop. Having thousands of items, exluded will reduce the digest load significantly.
很有可能,在更新的角度版本中,你的代码会运行得更快。对于角度版本1.3,有一个绑定-once操作符::,它将从摘要循环中排除一次性绑定的变量。有了数千个项目,exluded将大大减少消化负荷。
As with ECMAScript 6, angular can use the Observe class, which will make the dirty comparisiion check totaly obsolete. So a single mouseover would result internally in a single event callback, no more apply or diggesting. All with the original code. When Angular will apply this, I dont know. I guess in 2.0.
与ECMAScript 6一样,角可以使用观察类,这将使脏比较检查totaly过时。因此,一个鼠标悬停将在内部产生一个事件回调,不再应用或diggesting。都是原始代码。当角作用于这个,我不知道。我想在2.0。
#3
7
This is an old question now, but I think it's worth adding to the mix that Angular (since v1.3) now supports one time binding which helps slim down the digest loop. I have worked on a few applications where adding one time binding reduced the number of watches dramatically which led to improved performance. ng-repeat is often responsible for adding a lot of watches, so you could potentially consider adding one time binding to the ng-repeat.
这是一个古老的问题,但我认为值得加入这个有棱角的组合(因为v1.3)现在支持一个时间绑定,它可以帮助简化摘要循环。我曾参与过一些应用程序,在这些应用程序中,增加一次时间绑定可以显著减少手表数量,从而提高性能。ng-repeat通常负责添加大量的手表,所以您可能会考虑添加一个时间绑定到ng-repeat。
ng-repeat="i in ::list"
Here is a summary of a few techniques that can be used to avoid adding unnecessary watches
下面总结了一些可以避免添加不必要手表的技术
http://www.syntaxsuccess.com/viewarticle/547a8ba2c26c307c614c715e
http://www.syntaxsuccess.com/viewarticle/547a8ba2c26c307c614c715e
#4
4
Always profile first to find the real bottleneck. Sometimes it might be not something you initially suspect. I would suspect your own code first, then Angular (high number of watchers being the main feature leading to sluggish performance). I described how to profile and solve different performance problems in an Angular app in detailed blog post https://glebbahmutov.com/blog/improving-angular-web-app-performance-example/
总是先配置文件以找到真正的瓶颈。有时它可能不是你最初怀疑的东西。我首先会怀疑您自己的代码,然后是有棱角的(大量的观察者是导致性能迟缓的主要特性)。我在详细的博客文章https://glebbahmutov.com/blog/improing-angular -web-app-performance-example/中描述了如何在一个有棱角的应用中描述和解决不同的性能问题
#5
0
Get zone.js from btford and run all functions in a zone to check their times, then create zones to handle ajax code (crud) and other zones for static code (apis).
区。来自btford的js在一个区域运行所有的函数来检查他们的时间,然后创建区域来处理ajax代码(crud)和其他静态代码区域(api)。
Alternatively, limiting ng-repeat and/or disabling two-way binding on objects goes a long way atm.. a problem that web components already covers by using shadow dom leaving the top crispy to touch. still zone.js - watch the video through the link on plausibilities.
或者,限制ng-repeat和/或禁用对象上的双向绑定会有很长的路要走。一个web组件已经通过使用阴影dom来覆盖的问题,它让顶部变得酥脆。还带。通过貌似可信的链接观看视频。
#6
-1
Well, I can see you are using $watch
. Angular recomends $watch whenever it is very much needed. SCenarios like updating a variable through ng-model
我看得出你在用$watch。有角度的回报$watch无论何时都是非常需要的。比如通过ng-model更新变量
http://jsfiddle.net/5qFzg/10/
Suraj
苏拉