suchen
HeimWeb-Frontendjs-TutorialOrganisieren Sie einige Leistungsoptimierungspunkte bei der Verwendung des AngularJS-Frameworks_AngularJS

1. Einleitung

Ganz gleich, ob Sie eine Legacy-Anwendung schreiben oder AngularJS in einer großen Anwendung übernehmen, die Leistung ist ein wichtiger Aspekt. Es ist wichtig zu verstehen, was die Verlangsamung von AngularJS-Anwendungen verursacht, und zu wissen, dass es wichtig ist, während der Entwicklung Kompromisse einzugehen. In diesem Artikel werden einige der häufigsten Leistungsprobleme von AngularJS vorgestellt und Optimierungsvorschläge gemacht.

2. Leistungstest-Tools

In diesem Artikel wird der Leistungstest-Benchmark von jsPerf http://jsperf.com/ verwendet.

3. Softwareleistung

Es gibt zwei grundlegende Faktoren zur Bewertung der Softwareleistung:

Der erste ist die zeitliche Komplexität des Algorithmus. Ein einfaches Beispiel ist, dass zwischen der linearen Suche und der binären Suche ein sehr erheblicher Leistungsunterschied besteht.

Der zweite Grund, warum Software langsam ist, ist die räumliche Komplexität. Dies ist, wie viel „Speicherplatz“ oder Arbeitsspeicher ein Computer benötigt, um Ihre Anwendung auszuführen. Je mehr Speicher benötigt wird, desto langsamer wird es ausgeführt.


4 Javascript-Leistung

Einige Leistungsprobleme werden nicht nur durch Angular verursacht, sondern sind JavaScript-bedingt.

4.1 Schleife

Vermeiden Sie den Aufruf von Funktionen innerhalb der Schleife und verschieben Sie sie auf externe Aufrufe.

var sum = 0;
for(var x = 0; x < 100; x++){
 var keys = Object.keys(obj);
 sum = sum + keys[x];
}

Die oben genannten Aspekte sind offensichtlich nicht so schnell wie die folgenden:

var sum = 0;
var keys = Object.keys(obj);
for(var x = 0; x < 100; x++){
 sum = sum + keys[x];
}

4.2 DOM-Zugriff

Seien Sie vorsichtig, wenn Sie DOM-Elemente abrufen

angular.element('div.elementClass')

Diese Methode ist sehr teuer. Tatsächlich verursacht dies in AngularJS keine großen Probleme. Aber es lohnt sich, aufmerksam zu sein. Der DOM-Baum sollte klein sein und der DOM-Zugriff sollte so klein wie möglich sein.

4.3 Garbage Collection mit variablem Umfang

Halten Sie Ihre Variablenbereiche so eng wie möglich, damit der Garbage Collector schneller Speicherplatz zurückgewinnen kann. Achten Sie auf folgende Fragen:

function demo(){
 var b = {childFunction: function(){
  console.log('hi this is the child function')
 };
 b.childFunction();
 return b;
}


Wenn diese Funktion endet, gibt es hier keinen Verweis auf b. b wird recycelt. Aber wenn es eine Zeile wie diese gibt:

var cFunc = demo();

Diese Referenz verhindert die Speicherbereinigung. Vermeiden Sie solche Verweise.

4.4 Arrays und Objekte

Viele Punkte hier:

Zum Beispiel:

for (var x=0; x<arr.length; x++) {
 i = arr[x].index;
}

Etwas schneller als dieses (beachten Sie, dass *arr ein Array ist, obj ein JSON-Objekt ist)

for (var x=0; x<100; x++) {
 i = obj[x].index;
}

Etwas schneller als dieses

var keys = Object.keys(obj);
for (var x = 0; x < keys.length; x++){
 i = obj[keys[x]].index;
}


5 重要的概念

我们已经讨论过有关JavaScript的性能,现在有必要看一看AngualrJS中的核心概念,看看它究竟是怎么运作的。

5.1 域(Scopes)和更新周期(Digest Cycle)

Angular的域本质上是一些JavaScript对象,它们从一些预定义的对象继承而来。基本上,小的域比大的域运行要快。

换句话说,每创建一个新的域,都会给垃圾回收器添加更多待回收的内容。

在写AngularJS应用中尤其要注意的一个核心概念和性能影响方面是更新周期(Digest Cycle)。实际上每一个域都会存放一个由方法组成的数组 $$watchers。

每当域中的一个值(属性)或绑定的DOM,如 ng-repeat,ng-switch 和 ng-if 等等,调用 $watch 时,一个函数(function)就会添加到相对应域中的$$watchers数组队列中。

当域中的值发生改变时,在$$watchers中所有的watchers函数都会被触发调用。并且当它们的任何一个修改了域中的某个值时,它们会被再次触发执行。

这个过程会一直循环下去直到$$watcher数组队列中不再做任何更改或抛出异常为止。

更外如果任何代码执行$scope.$apply(),都会触发更新周期。

最后一点是 $scope.evalAsync() 会在一个异步调用中执行,并且在当前和下个执行周期中,不会调用其的更新周期。


6. 在设计Angular时应该遵守的一般准则

6.1 大型对象和服务器调用

所以这些都告诉了我们什么?首先我们要尽可能地简化我们的对象。当对象是从服务器返回时,这一点尤为重要。

直接将数据库中的一行转换成对象只是临时性方案,因此不要使用.toJson().

只需要把Angular需要的属性值返回回来。

6.2 监视函数(Watching Functions)

另一个常见的问题是为观察者绑定的函数。不要将任何东西(ng-show, ng-repeat等等)直接绑定到一个函数。不要直接监视任何函数的返回值。该函数会在每个更新周期都执行,可能会降低你应用的速度。

6.3 监视对象(Watching Objects)

同样,Angular提供了第三个可选参数来监视整个对象的改动。将调用$watch的第三个参数设为true。这是一个非常可怕的想法。一个更好的解决办法是依靠服务和对象的引用,监视域之间的变化。

7 列表问题

7.1 长列表(Lists)

尽一些可能避免长列表。ng-repeat会进行了一些很重的DOM操作(更不用说对$$watchers的污染),所以无论是在分页或是在无限滚动中,尽量使用小型数据进行渲染。

7.2 过滤器(Filters)

要尽量避免使用过滤器。他们会在每个更新周期运行两次,每当发生任何改变时运行一次,另一次是收集更深层次的改变时触发。所以不要直接从内部列表中移除对象,使用CSS控制即可。(注* 用添加CSS类名去隐掉他们)

渲染时的 $index 值并不是真正的数组索引值,它豪无价值。但是排好序的数组索引,无法让你遍历到所有列表中的域。

7.3 更新 ng-repeat

当使用ng-repeat时要尽量避免对全局列表的刷新。ng-repeat会产生一个$$hashkey属性和一系统唯一的项。这意味着当你调用 scope.listBoundToNgRepeat = serverFetch() 时会引起对整个列表的重新刷新。会通知执行所有的watchers并触发每一个元素,这是非常消耗性能的。

这里有两种解决方案。一种是维护两个集合,和带有过虑器(filter)的ng-repeat(基本上需要自定义同步逻辑,因此算法更复杂,可维护性更差),另一种方案是使用track by去指定你自己的key(Angular 1.2 开始支持,只需要很少的同步逻辑)。

总之:

scope.arr = mockServerFetch();

会比下面的这种慢

var a = mockServerFetch();
for(var i = scope.arr.length - 1; i >=0; i--){
 var result = _.find(a, function(r){
 return (r && r.trackingKey == scope.arr[i].trackingKey);
 });
 if (!result){
 scope.arr.splice(i, 1);
 } else {
 a.splice(a.indexOf(scope.arr[i]), 1);
 } 
}
_.map(a, function(newItem){
 scope.arr.push(newItem);
});


这种

<div ng-repeat="a in arr track by a.trackingKey">


比上面的慢些

<div ng-repeat="a in arr">

8 渲染问题

另一个引起Angular应用慢的原因是不正确地使用 ng-hide/ ng-show 或 ng-switch。

ng-hide 和 ng-show 简单地对CSS display属性进行切换。这意味着表面上看不见的东西其实还存在于域中, 所有的$$watchers还是会被触发。

ng-if 和 ng-switch实际上从DOM中完全移除了,相应的域也会被移除。性能差异显而易见。

9. 更新周期问题

9.1 绑定

尽量减少你的绑定。在Angular 1.3中这里有一个新的一次绑定语法,{{::scopeValue}}。它只会被域执行一次,并不添加到监视器要监视列表中(watcher array).

9.2 $digest() 和 $apply()

scope.$apply 是一个强大的工具,可以让你向Angular引入外部的值。本质上它会触发Angular的所有事件(例如ng-click)。问题是scope.$apply会从根域$rootScope开始,遍历所有的域链,触发每一个域。

scope.$digest只会执行指定域及其相关的域。两种性能差异不言自明。折中的方案是,不触发任何域等到下一个更新周期再更新。

9.3 $watch()

scope.$watch() 已经在很多场景被讨论过的。基本上scope.$watch是不好的设计的一个标志。如果你非要创建一个观察者。记住对它尽可能地解绑。你可以用$watch的返回函数解绑。

var unbinder = scope.$watch('scopeValueToBeWatcher', function(newVal, oldVal) {
 
});
unbinder(); //这一行将watcher从 $$watchers 中移除。

如果你不能早一点解绑,记住在 $on('$destroy') 中进行解绑。

9.4 $on, $broadcast 和 $emit

像$watch一样,他们都是一些很慢的事件,(有可能)遍历整个作用域。他们可能像GOTO一样,让你的程序无法调试。不过幸运地是像$watch一样,他们都可以在完全不需要的时侯解绑。比如在 $on('$destroy')中。

9.5 $destroy

像前面提到的那样,你应该在$on('$destroy')中解绑你所有的事件侦听器,取消任何$timeout的实例,或者任何其它异步执行的交互。这不仅仅是确保安全。还可以让你的域更快地被垃圾回收。不这样做,他们会一直在后台运行。直接你清空CPU和RAM。

另外,解绑DOM上的事件侦听器也非常重要,不这样做很可能在老式浏览器中引起内存泄露。

9.6 $evalAsync

scope.$evalAsync是一个强大的工具。它可以在当前域中执行,并不触发域的更新。evalAsync可以极大地提高你网页的性能。

10 指令问题

10.1 隔离的域(Isolate Scope)和Transclusion

域隔离和Transclusion是Angular最另人激动的特性,它们是Angular的核心组件。

但是这里也有一些权衡,指令不能直接创建一个替换他们父组元素的域。通过隔离的域或Transclusion我们可以创建一个新的对象去跟踪,添加新的监视器,但是这也会降低应用的性能。在添加之前应该仔细想一想有没有这个必要。


10.2 编绎周期

指令(Directive)的compile函数是在域被附加前操作DOM的完美功能(比如说绑定事件)。一个很重要的性能方面是,传入compile函数的元素和属性以原始html模板呈现。只会被运行一次,接下来会直接使用。另外一个重要的点是prelink和postlink的区别。prelink从外向内执行。postlinks从内向外执行。prelink性能稍好一些,因为它不会产生第二次更新周期。但是这时子元素的DOM还未被创建。


11 DOM事件问题

Angular提供了很多预定义的DOM事件指令。ng-click,ng-mouseenter,ng-mouseleave等等。当调用scole.$apply()时这些事件都会被执行。另外一种更有效率的方式是直接在DOM上面绑定addEventListener,并且尽量使用scope.$digest

优化实例


测试一个应用框架确实是个严峻的挑战,当用户点击日志中任何一个单词,我们就要搜索出相关信息,而页面上可以点击的元素又不计其数;我们想让日志的分页功能也瞬间得到反馈。我们其实已经预先获取到了下一页面的日志数据,所以用户接口的更新就成为了瓶颈,如果拿 AngularJS直接实现日志视图的换页功能需要1.2秒,但是如果仔细优化一下的话就可以降到35毫秒。这些优化被证明在应用的其他部分也是适用的,并且对AngularJS适应性也很好。但我们必须打破一些规则来实现我们的想法,稍后讨论。

201635161723160.jpg (590×558)

一个Github更新的日志demo

An AngularJS log viewer

本质上,日志视图就是一个日志消息的列表,每个字都可以点击。所以把Angular的指令加到DOM元素中,简单实现如下:

<span class='logLine' ng-repeat='line in logLinesToShow'>
 <span class='logToken' ng-repeat='token in line'>{{token | formatToken}} </span>
 
</span>

在单页面应用中有个数千个tokens是很正常的,在早期的测试中,我们发现进入日志的下一页会花费好几秒来执行JavaScript。更糟的是,不相关的操作(比如点击导航下拉框)延迟也不轻,AngularJS的大神说最好把数据元素绑定的数量控制在200以下。对于一个单词就是一个元素的我们来说,早已远超这个数。

分析:

用Chrome的JavaScript profiler工具,我们可以快速定位两个拖延点。首先,每次更新要花大量时间在DOM元素的创建和销毁上,如果新的view有不同的行数,或者任何一行有不同数量单词,Angular的ng-repeat指令就会创建或者销毁DOM元素,这个代价太大了。

其次,每一个单词都有自己的change watcher,AngularJS会watch这些单词,一旦鼠标点击就会触发,这个是影响不相关操作(下拉菜单导航)延迟的罪魁祸首。

优化#1:缓存DOM elements

我们创建了一个ng-repeat指令的变体,在我们的版本中,如果绑定数据的数量减少了,超出的DOM元素会隐藏而不是销毁,如果元素的数量过会儿有增加了,我们会重用这些缓存的元素。

优化#2:Aggregate watchers

用来调用change watchers的所有时间大部分都浪费了,在我们的应用中,特定单词上的数据绑定都是永远不会改变的除非整个日志消息变化,为了达成这一点,我们创建了一个指令”hides“隐藏掉了子元素的change watchers,只有等特定父元素表达式修改的时候才会调用他们。就这样,我们避免了在每一次鼠标点击或者其他微小的修改而导致的全盘change watchers(为了实现这个想法,我们稍微修改了AngularJS的抽象层,我们稍后再细说)。

优化#3:推迟元素创建

前面说了,我们为日志里的每一个单词单独创建了DOM,我们可以利用每一行的单个DOM元素得到相同的视觉呈现;其他元素都是为响应鼠标点操作而创建的,因此,我们决定推迟这部分创建,只有当鼠标移动到某行的时候我们再创建他。

为了实现这个,我们为每一行创建了两个版本,一个就是简单的文本元素来显示完整的日志信息,另外一行就是个占位符,用来显示最终为每一个单词填充后的效果。这个占位符开始是隐藏的,当鼠标移动到那一行的时候才会显示,而简单文本那一行这个时候就隐藏掉。下面会讲到,显示占位符是如何填充单词元素的。

优化#4:避开对隐藏元素的监视

我们创建了另外一个指令,用来阻止对隐藏元素的监视,这个指令支持优化#1,相较于原数据,我们多了更多的隐藏DOM节点,所以必须消除对多出来的DOM节点的监视。这也支持优化#3,让推迟单词节点的创建更加容易。因为直到这行数据的tokenized版本出现我们才会创建他 。

下面的代码就是所有的优化后的样子,我们自定义的指令是粗体显示。

<span class='logLine' sly-repeat='line in logLinesToShow' sly-evaluate-only-when='logLines'>
 <div ng-mouseenter=”mouseHasEntered = true”>
  <span ng-show='!mouseHasEntered'>{{logLine | formatLine }} </span>
  <div ng-show='mouseHasEntered' sly-prevent-evaluation-when-hidden>
   <span class='logToken' sly-repeat='tokens in line'>{{token | formatToken }}</span>
  </div>
 </div>
 
</span>

Sly-repeat 是ng-repeat的变体,用来隐藏多出来的DOM元素而不是销毁他们,sly-evaluate-only-when阻止内部change watchers除非“logLines”变量修改,sly-prevent-evaluation-when-hidden主要负责当鼠标移动到指定行的上面的时候,隐藏的div才显示。

这里展示出了AngularJS对于封装和分离的控制力,我们做了复杂的优化但是并没有影响模板的结构(这里展示的代码并不是真正产品里的代码,但是他展示了所有的要点)。

结果:

我们来看一下效果,我们添加了一些代码来衡量,从鼠标点击开始,一直到Angular's $digest循环结束(意味着更新DOM结束)。

我们衡量点击”下一页“按钮的性能是通过Tomcat日志,环境用的是MacBook Pro上的Chrome,结果见下表(每个数据都是10次测试的平均值):

数据已经缓存 从服务器获取数据
简单实现 1190 ms 1300 ms
优化后 35 ms 201 ms

In diesen Zahlen ist die Zeit nicht enthalten, die der Browser für das Layouten und Neuzeichnen des DOM aufwendet (nach Abschluss der JavaScript-Ausführung), die jedes Mal etwa 30 Millisekunden beträgt. Dennoch ist der Effekt spürbar; die Reaktionszeit für die nächste Seite sinkt von 1.200 Millisekunden auf 35 Millisekunden (65 Millisekunden, wenn man das Rendering mit einbezieht).

Die Daten unter „Daten vom Server abrufen“ umfassen die Zeit, die wir mit AJAX verwenden, um Protokolldaten vom Backend abzurufen. Dies unterscheidet sich vom Klicken auf die Schaltfläche „Nächste Seite“, da wir die Protokolldaten für die nächste Seite vorbereiten, es kann jedoch auch auf andere UI-Antworten anwendbar sein. Trotzdem kann das optimierte Programm in Echtzeit aktualisiert werden.

Stellungnahme
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
电脑性能看哪些方面电脑性能看哪些方面Dec 23, 2020 pm 01:54 PM

电脑性能看如下几个方面:1、电脑安装的操作系统的版本;2、电脑所配置的处理器类型;3、电脑安装的内存大小;4、操作系统是32位的还是64位的。

Vue3中的lazy函数详解:懒加载组件提高应用性能Vue3中的lazy函数详解:懒加载组件提高应用性能Jun 19, 2023 am 08:39 AM

Vue3是一款流行的JavaScript框架,它具有易学易用、高效稳定的特点,尤其擅长构建单页应用程序(SPA)。Vue3中的lazy函数,作为懒加载组件的利器之一,可以很大程度上提高应用程序的性能。本文将详解Vue3中的lazy函数的使用方法与原理,以及它在实际开发中的应用场景和优点。什么是懒加载?在传统的前后端分离的开发中,前端开发人员往往需要处理大量的

如何通过设置MySQL缓存来提高性能如何通过设置MySQL缓存来提高性能May 11, 2023 am 08:09 AM

MySQL是一种常用的关系型数据库管理系统(RDBMS),在各种应用场景下都得到广泛的应用。然而,在高并发、大数据量的情况下,MySQL数据库的性能受到挑战,特别是在读写操作频繁的场景下,容易出现性能瓶颈。为了提高MySQL数据库的性能,可以通过设置MySQL缓存来减少数据库的IO操作,从而提高MySQL的查询效率。在本文中,我们将介绍如何通过设置MySQL

2022年最新5款的angularjs教程从入门到精通2022年最新5款的angularjs教程从入门到精通Jun 15, 2017 pm 05:50 PM

Javascript 是一个非常有个性的语言. 无论是从代码的组织, 还是代码的编程范式, 还是面向对象理论都独具一格. 而很早就在争论的Javascript 是不是面向对象语言这个问题, 显然已有答案. 但是, 即使 Javascript 叱咤风云二十年, 如果想要看懂 jQuery, Angularjs, 甚至是 React 等流行框架, 观看《黑马云课堂JavaScript 高级框架设计视频教程》就对了。

一篇学会本地知识库对LLM的性能优化一篇学会本地知识库对LLM的性能优化Jun 12, 2023 am 09:23 AM

昨天一个跑了220个小时的微调训练完成了,主要任务是想在CHATGLM-6B上微调出一个能够较为精确的诊断数据库错误信息的对话模型来。不过这个等了将近十天的训练最后的结果令人失望,比起我之前做的一个样本覆盖更小的训练来,差的还是挺大的。这样的结果还是有点令人失望的,这个模型基本上是没有实用价值的。看样子需要重新调整参数与训练集,再做一次训练。大语言模型的训练是一场军备竞赛,没有好的装备是玩不起来的。看样子我们也必须要升级一下实验室的装备了,否则没有几个十天可以浪费。从最近的几次失败的微调训练来看

自动驾驶决策规划技术详解自动驾驶决策规划技术详解Apr 04, 2023 pm 02:35 PM

随着深度强化学习技术的快速发展,越来越多的研究团队开始将其应用于自动驾驶决策规划中,将行为决策与运动规划模块相融合,直接学习得到行驶轨迹。 自动驾驶中的决策规划模块是衡量和评价自动驾驶能力最核心的指标之一,它的主要任务是在接收到传感器的各种感知信息之后,对当前环境作出分析,然后对底层控制模块下达指令。典型的决策规划模块可以分为三个层次:全局路径规划、行为决策、运动规划。01 引言在一套完整的自动驾驶系统中,如果将感知模块比作人的眼睛和耳朵,那么决策规划就是自动驾驶的大脑。大脑在接收到传感器的各种

使用PHP和AngularJS搭建一个响应式网站,提供优质的用户体验使用PHP和AngularJS搭建一个响应式网站,提供优质的用户体验Jun 27, 2023 pm 07:37 PM

在如今信息时代,网站已经成为人们获取信息和交流的重要工具。一个响应式的网站能够适应各种设备,为用户提供优质的体验,成为了现代网站开发的热点。本篇文章将介绍如何使用PHP和AngularJS搭建一个响应式网站,从而提供优质的用户体验。PHP介绍PHP是一种开源的服务器端编程语言,非常适用于Web开发。PHP具有很多优点,如易于学习、跨平台、丰富的工具库、开发效

TPU 与 GPU:实际场景下性能和速度的比较差异TPU 与 GPU:实际场景下性能和速度的比较差异Apr 25, 2023 pm 04:34 PM

在本文中,我们将进行TPU与GPU的比较。但在我们深入研究之前,这是你必须知道的。机器学习和人工智能技术加速了智能应用的发展。为此,半导体公司不断创建加速器和处理器,包括TPU和CPU,以处理更复杂的应用程序。一些用户在理解何时建议使用TPU以及何时使用GPU来完成他们的计算机任务时遇到了问题。GPU也称为图形处理单元,是您PC的视频卡,可为您提供视觉和身临其境的PC体验。例如,如果您的PC未检测到GPU,您可以按照简单的步骤操作。为了更好地理解这些情况,我们还需要澄

See all articles

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Sicherer Prüfungsbrowser

Sicherer Prüfungsbrowser

Safe Exam Browser ist eine sichere Browserumgebung für die sichere Teilnahme an Online-Prüfungen. Diese Software verwandelt jeden Computer in einen sicheren Arbeitsplatz. Es kontrolliert den Zugriff auf alle Dienstprogramme und verhindert, dass Schüler nicht autorisierte Ressourcen nutzen.

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Leistungsstarke integrierte PHP-Entwicklungsumgebung

SublimeText3 Englische Version

SublimeText3 Englische Version

Empfohlen: Win-Version, unterstützt Code-Eingabeaufforderungen!

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools