angular项目优化,angularjs性能优化
Angular怎么优化?下面这篇文章给大家看看Angular中的性能优化,希望能帮到你!
本文将谈谈Angular的性能优化,主要介绍与运行时相关的优化。在谈如何优化之前,首先我们要知道什么样的页面存在性能问题。衡量业绩好坏的标准是什么?性能优化背后的原理是什么?如果你对这些问题感兴趣,那么请继续读下去。【相关教程推荐:《angular教程》】
变更检测机制
与网络传输优化不同,运行时优化更关注Angular的运行机制,以及如何对其进行编码以有效避免性能问题(最佳实践)。要了解Angular的运行机制,首先要了解它的变化检测机制(也称为脏检查)3354是如何将状态变化重新渲染到视图中的。如何将组件状态的变化反映到视图中也是三个前端框架都需要解决的问题。不同框架的解决方案有相似的思路,也有各自的特点。
首先,Vue和React都使用虚拟DOM来更新视图,但是在具体实现上有一些区别:
对于React:
使用setState或forceUpdate触发render方法来更新视图
当父组件更新视图时,它还会确定是否需要重新渲染子组件。
使用Vue:
Vue将遍历数据对象的所有属性,并使用Object.defineProperty将它们转换成包装的getter和setter。
每个组件实例都有一个对应的watcher实例对象,该对象在组件渲染过程中将属性记录为依赖关系。
当一个依赖项的setter被调用时,它会通知watcher重新计算,这样它的关联组件就可以被更新。
Angular通过引入Zone.js来修补异步操作的API,并监控其对变化检测的触发。Zone.js的原理在上一篇文章中有详细介绍。简单来说,Zone.js通过Monkey patch对浏览器或节点中的所有异步API进行暴力封装和替换。
如浏览器中的setTimeout:
let originalSetTimeout=window . settimeout;
window.setTimeout=function(回调,延迟){
return originalSetTimeout(zone . current . wrap(回调),delay);
}
Zone.prototype.wrap=function(回调){
//获取当前区域
设capturedZone=this
返回函数(){
返回capturedZone.runGuarded(回调,this,arguments);
};
};或无极法:
let original promise then=promise . prototype . then;
//注:此处简化。实际上,那时可以接受更多的参数。
promise . prototype . then=function(回调){
//获取当前区域
设capturedZone=Zone.current
函数wrappedCallback() {
返回capturedZone.run(回调,this,arguments);
};
//触发capturedZone中的原始回调
返回originalPromiseThen.call(this,[wrapped callback]);
};Zone.js在加载时封装了所有异步接口。因此,在Zone.js中执行的所有异步方法都会作为一个任务被监管,并提供相应的钩子,在异步任务执行前后或某个阶段做一些额外的操作。因此,通过Zone.js可以方便地实现日志记录、性能监控和控制异步回调时机等功能
而这些钩子函数(hooks)可以通过Zone.fork()方法来设置。有关详细信息,请参考以下配置:
zone . current . fork(zonepec)//zone pec的类型是zone pec。
//只有名称是必需的,其他是可选的。
界面区域规范{
名称:字符串;//区域的名称,通常在调试区域时使用。
属性?{[key:string]:any;} ;//可以通过Zone.get(key )获取一些可以附加到区域的数据
onFork:函数;//当区域分叉时,触发此函数
onIntercept?功能;//拦截所有回调
onInvoke?功能;//当回调被调用时,触发这个函数
onHandleError?功能;//统一处理异常。
onScheduleTask?功能;//调度任务时触发该函数。
onInvokeTask?功能;//当任务被触发执行时,函数被触发
onCancelTask?功能;//任务取消时触发此函数
onHasTask?功能;//通知任务队列的状态变化
}举个onInvoke的简单例子:
let logZone=Zone.current.fork({
名称:日志区域,
on invoke:function(parentZoneDelegate,currentZone,targetZone,Delegate,applyThis,applyArgs,source) {
console.log(targetZone.name, enter );
parentzoneDelegate . invoke(target zone,delegate,applyThis,applyArgs,source)
console.log(targetZone.name, leave );}
});
logZone.run(函数myApp() {
console.log(Zone.current.name,队列承诺);
Promise.resolve(OK )。然后((值)={ console . log(zone . current . name, Promise ,value)
});
});最终执行结果:
了解了Zone.js的原理后,通过阅读Angular的源代码,我们可以知道,在Angular中使用Zone.js是为了实现每当调用异步方法或事件时,都会触发变化检测。一般如下:
首先,在文件applicatoin _ ref.ts中,ApplicationRef构建时,订阅了微任务队列为空的回调事件,调用tick方法(即变化检测):
其次,在checkStable方法中,会判断微任务队列清空时会触发onMicrotaskEmpty事件(组合起来相当于触发变更检测):
最后,可以触发checkStable方法调用的地方在Zone.js的三个hook函数中,分别是onInvoke、onInvokeTask和onHasTask:
例如,onHasTask ——在检测到存在或不存在ZoneTask时触发的挂钩:
此外,Zone.js中有三种类型的异步任务:
Micro Task(微任务):是由诺言等创造的。原生承诺将在当前事件周期结束前执行,修补承诺也将在事件周期结束前执行。
Macro Task (宏任务):由setTimeout等创建。本机setTimeout将在未来的某个时间处理。
Event Task :由addEventListener等创建。这些任务可能被触发多次,也可能永远不会被触发。
其实从浏览器的角度来说,Event Task其实可以算是一个宏任务。换句话说,所有事件或异步API都可以理解为宏任务或微任务之一,它们的执行顺序在之前的文章中已经详细分析过了。简单来说:
(1)主线程执行后,优先检查微任务队列中是否有要执行的任务。
(2)第一次轮询后,会检查宏任务队列中是否还有任务,执行后检查微任务列表中是否有任务,然后重复这个过程。
性能优化原理
对页面性能最直观的判断就是页面响应是否流畅快速。本质上,页面响应是将页面状态的变化重新呈现给页面的过程。从相对宏观的角度来看,Angular的变化检测其实只是整个事件响应周期中的一个环节。用户与页面的所有交互都是由事件触发的,整个响应过程大致如下:
如果考虑优化页面响应速度,可以从每个阶段开始:
(1)对于触发事件阶段,可以减少事件的触发,以减少变化检测和重新呈现的总数。
(2)对于事件处理程序执行逻辑阶段,可以通过优化复杂的代码逻辑来减少执行时间。
(3)对于变化检测检测数据绑定和更新DOM的阶段,可以减少变化检测和模板数据的计算次数以减少渲染时间。
(4)对于浏览器渲染阶段,可能需要考虑使用不同的浏览器或者升级硬件配置。
这里不多讨论第二第四阶段的优化。结合上述角度对异步任务的分类,可以进一步明确第一阶段和第三阶段的优化方法:
(1)对于宏任务合并请求,尽量减少勾选次数。
(2)为微任务合并tick
(3)为事件任务减少事件的触发和注册事件。
(4)tick分为检查和渲染两个阶段,减少了检查阶段的计算和不必要的渲染。
如前所述,在大多数情况下,可以通过观察页面是否流畅来判断页面是否存在性能问题。这种方法虽然简单直观,但也比较主观,而不是通过准确的数字来反映页面的表现。换句话说,我们需要一个更有效、更准确的指标来衡量什么样的页面有好的性能。Angular官方也提供了相应的方案,可以通过打开Angular的调试工具来监控变化检测周期(完成滴答)的持续时间。
首先,您需要使用Angular提供的enableDebugTools方法,如下所示:
之后只需要在浏览器的控制台输入ng . profiler . timechangeedetection()就可以看到当前页面的平均变化检测时间:
从上面可以看出,692个变化检测周期(完整的事件响应周期)的平均时间是0.72毫秒。如果你运行几次,你会发现每次运行的总次数是不一样的,是随机的。
官方提供了这样一个标准:理想情况下,分析器打印出来的时间长度(单个变化检测周期的时间)应该远低于单个动画帧的时间(16ms)。一般来说,这个持续时间保持在3毫秒,这意味着当前页面的变化检测循环的性能相对较好。如果超过这个时间,可以结合Angular的变化检测机制,分析是否存在重复模板计算和变化检测。
性能优化方案
在了解角度优化原理的基础上,我们可以更有针对性地优化相应的性能:
(1)针对异步任务 ——减少变更检测的次数
使用NgZone的runOutsideAngular方法执行异步接口手动触发Angular(2)针对 Event Task —— 减少变更检测的次数的变化检测。
将输入等事件转换为触发频率较低的事件以稳定图像输入值改变的事件不能减少变化检测的次数。
如上图所示,稳像器进程只是保证代码逻辑不会重复运行,但是valueChanges的事件是随着值的变化而触发的(如果变化几次,就会触发几次),只要有事件触发,就会相应地触发变化检测。
(3)使用 Pipe ——减少变更检测中的计算次数
将管道定义为纯管道(@Pipe默认为纯管道,因此也可以设置pure: true而不显示它)
从“@angular/core”导入{ Piep,pipe transform };
@管道({
姓名:性别,
纯粹的,
})
导出类GenderPiep实现PipeTransform {
转换(值:字符串):字符串{
If (value===M )返回 male ;
If (value===W )返回女性;
返回“”;
}
}关于纯/不纯管道:
Pure Pipe:如果进管参数没有变化,直接返回之前的计算结果。
010-5900每次更改检测都将重新运行管道内部的逻辑并返回结果。(简单地说,不透水管道相当于一个普通的格式化函数。如果一个页面触发了多个变更检测,那么不可渗透管道的逻辑将被执行多次。)
ImPure Pipe:
使用组件onPush模式,组件将只在输入属性改变时进行检测。只有当组件或其子组件中的DOM事件被触发时,其他非DOM事件的异步事件才会被触发。仅手动触发对声明onPush的子组件的检测。如果输入属性不变,计算和更新@Component({
.
change detection:changeedetectionstrategy。OnPush,
})
导出XXXComponent类{
.
} Angular中显示的@Component的changeDetection设置为ChangeDetectionStrategy。OnPush打开OnPush模式(默认情况下不打开)。使用on push,您可以跳过组件或父组件及其所有子组件的变更检测,如下所示:
(4)针对组件 ——减少不必要的变更检测
列表的循环呈现使用trackBy尽可能使用缓存的值,避免使用方法调用和get属性。如果调用模板中确实有需要调用函数的地方,并且有多次调用,可以缓存ngIf来控制组件的显示,放在控制调用组件的地方,(5)针对模板 ——减少不必要的计算和渲染。
不要用try/catch进行进程控制,这样会造成大量的时间消耗(记录大量的栈信息等。).过多的动画会导致页面加载一个很长的列表。您可以使用虚拟滚动来尽可能延迟预加载模块的加载,因为浏览器中并发http请求线程的数量是有限的。一旦超过限制,后续的请求将被阻塞和挂起,等等。(6)其他编码优化建议
(1)简要说明Angular如何使用Zone.js实现变化检测。
(2)在了解Angular的变化检测的基础上,进一步明确了Angular性能优化的原则和判断页面是否具有良好性能的标准。
(3)给出了部分运行时的性能优化方案。
有关编程的更多信息,请访问:编程入门!Angular就是这么优化的。性能优化方案分析详情请多关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。