angular如何进行性能优化?变更检测方式浅析

angular如何进行性能优化?下面本篇文章给大家深入介绍一下angular 性能优化方案–变更检测,希望对大家有所帮助!

angular如何进行性能优化?变更检测方式浅析

angular 性能优化——变更检测

         对于前端性能指标描述,业界都各有说词,总结下来都和首屏性能和页面流畅度相关,本次将会从页面流畅度的角度,对页面交互性能优化进行分析。【相关教程推荐:《angular教程》】

什么是页面流畅度?

        页面流畅度是通过帧率 FPS(Frames Per Second – 每秒传输帧数)判定的,一般主流的浏览器屏幕刷新率都在 60Hz(每秒刷新60次),最优的帧率在 60 FPS,帧率越高,页面就越流畅,60Hz意味着每隔16.6ms会刷新一次显示屏,也就是每一次渲染页面需要在16.6ms内完成,否则就会导致页面失帧,出现卡顿现象。根因在于:浏览器中的 JavaScript 执行和页面渲染会相互阻塞

         在 Chrome 的 devtools 中我们可以执行 Cmd+Shift+P 输入 show fps 来快速打开 fps 面板,如下图所示:

1.png

通过观察 FPS 面板,我们可以很方便的对当前页面的流畅度进行监控

2.png

1 影响页面性能的因素

         页面交互是否流畅,在于页面响应是否流畅,而页面响应其本质上就是把页面状态的变更重新渲染到页面上的过程。

         页面响应过程大致如下:

3.png

一般情况Event Handler事件处理逻辑不会消耗太多时间,所以影响angular性能的因素主要在于异步事件触发变更检测。一般情况Event Handler事件处理逻辑不会消耗太多时间,所以影响angular性能的因素主要在于异步事件触发和变更检测。

         对angular来说,页面渲染的过程就是变更检测的过程,可以理解为angular的变更检测要在16.6ms内完成才不会导致页面失帧、卡顿。

可以从以下三方面优化页面响应的性能。

(1)对于触发事件阶段,可以减少异步事件的触发,来减少整体的变更检测次数和重新渲染; 

(2)对于 Event Handler 执行逻辑阶段,可以通过优化复杂代码逻辑来减少执行时间;

(3)对于 Change Detection 检测数据绑定并更新 DOM 阶段,可以减少变更检测模板数据的计算次数来减少渲染时间;

对于(2)Event Handler要具体问题具体分析,不做讨论,主要针对(1)(3)进行优化

2 优化方案

2.1 减少异步事件触发

         Angular在默认变更检测模式下,异步事件会触发全局的变更检测,因此,减少异步事件的触发会大大的提升angular的性能。

         异步事件包括Macro Task(宏任务)事件和Micro Task微任务事件

4.png

         对异步事件的优化主要是针对document的监听事件。比如document上的click、mouseup、mousemove…等监听事件。

         监听事件场景:

                Renderer2.listen(document, …)

                fromEvent(document,…)

                document.addEventListener(…)

         dom监听事件,在不需要触发的时候一定要移除。

举例:[pop]提示框指令

         使用场景:表格列筛选,点击图标以外的地方,或者页面滚动,列筛选弹框隐藏

         以前的做法是直接在pop指令里监听document的click事件和scroll事件,这样有个弊端就是提示框未显示,但依然存在监听事件,很不合理。

         合理的解决方案:当提示框显示的时候才去监听click和scroll事件,隐藏的时候就移除监听事件。

5.png

对于频繁触发的dom监听事件,可以使用rjx的操作符对事件进行优化。详情参考Rjx操作符。RxJS Marbles。

2.2 变更检测

什么是变更检测?

         要理解变更检测,我们可以从变更检测的目标寻找答案。angular变更检测目标,是让模型(TypeScript代码)与模板(HTML)保持同步。因此,变更检测可以理解为:检测模型变更的同时,更新模板( DOM

变更检测流程是什么?

6.png

       通过在组件树中按照自顶向下的顺序执行变更检测,也就是先对父组件执行变更检测,再对子组件进行变更检测。首先检查父组件的数据变更,然后更新父组件模板,在更新模板的时候遇到子组件,会去更新子组件上绑定的值,然后进入子组件,看@Input输入值的索引是否改变,如果改变就将该子组件标记为dirty,也就是后续需要变更检测的,标记完子组件之后,继续更新父组件中子组件后面的模板,父组件模板全部更新完之后再去对子组件做变更检测。

2.2.1 angular变更检测原理   

       在默认变更检测default模式下,异步事件触发Angular的变更检测的原理是 angular通过使用Zone.js处理异步事件时调用了ApplicationRef 的tick()方法从根组件到子组件执行变更检测。 Angular 应用初始化过程中,实例化了一个zone (NgZone),然后将所有逻辑都跑在该对象的 _inner 对象中。

Zone.js实现了以下几个类:

Zone类,JavaScript 事件的执行环境,和线程一样,它们可以带一些数据,并且可能拥有父子 zone。ZoneTask类,包装后的异步事件,这些 task 有三种子类:MicroTask,由 Promise 创建。MacroTask,由 setTimeout 等创建。EventTask,由 addEventListener 等创建,比如dom事件。ZoneSpec对象,创建一个 ngZone 时给它提供的参数,有三个可以触发检测的钩子:onInvoke,调用某个回调函数时触发的钩子。onInvokeTask,ZoneTask 被触发时触发的钩子,比如 setTimeout 到时。onHasTask,检测到有或无 ZoneTask 时触发的钩子(即对第一个 schedule 的 zone 和最后一个 invoke 或 cancel 的 task 触发)。ZoneDelegate类,负责调用钩子。

检测过程原理大概如下:

       用户操作触发异步事件(比如:dom事件,接口请求…)

=>  ZoneTask类处理事件。invokeTask()函数中调用zone的runTask()方法,在runTask方法中,zone通过_zoneDelegate实例属性,调用ZoneSpec的钩子

=>  ZoneSpec的三个钩子(onInvokeTask、onInvoke、onHasTask)钩子里通过checkStable()函数触发zone.onMicrotaskEmpty.emit(null)通知

=>  根组件监听onMicrotaskEmpty后调用tick(),tick方法中调用 detectChanges()从根组件开始检测

=> ··· refreshView()调用executeTemplate()executeTemplate方法中调用templateFn()更新模板、子组件绑定的值(这时候会去检测子组件的@Input()输入引用是否改变,如果有改变,会将子组件标记为Dirty,也就是该子组件需要变更检测

详细变更检测原理流程图:

7.png

简化流程:

触发异步事件

行者AI 行者AI

行者AI绘图创作,唤醒新的灵感,创造更多可能

行者AI 100 查看详情 行者AI

=>  ZoneTask处理事件

=> ZoneDelegate 调用ZoneSpec的钩子触发onMicrotaskEmpty通知

=> 根组件收到onMicrotaskEmpty通知,执行tick(),开始检测并更新dom

8.png

由以上代码可知,当微任务为空的时候才会触发变更检测

简略变更检测原理流程图:

9.png

Angular 源码解析 Zone.js参考blog。

2.2.2 变更检测优化方案

1 )使用OnPush 模式

原理:减少1次变更检测的耗时。

         OnPush模式与Default模式的区别在于:dom监听事件、timer事件、promise都不会触发变更检测。Default模式的组件状态始终为CheckAlways,表示组件每次检测周期都要检测。

OnPush模式下:以下情况会触发变更检测

    S1、组件的@Input引用发生变化。

    S2、组件的DOM绑定的事件,包括它子组件的DOM绑定的事件,比如 click、submit、mouse down。@HostListener()

      注意:

        通过renderer2.listen()监听的dom事件不会触发变更检测

        通过dom.addEventListener()原生监听方式也不会触发变更检测

    S3、Observable 订阅事件,同时设置 Async pipe。

    S4、利用以下方式手动触发变化检测:

      ChangeDetectorRef.detectChanges():触发当前组件和非OnPush子组件的变更检测。

      ChangeDetectorRef.markForCheck():将当前视图及其所有的祖先标记为脏,下次检测周期时候会触发检测。

      ApplicationRef.tick():不会触发变更检测

2 )使用NgZone.runOutsideAngular()

原理:减少变更检测次数

         将全局dom事件监听写在NgZone.runOutsideAngular()方法的回调里面,dom事件将不会触发angular的变更检测。如果当前组件未更新,可以在回调函数里执行ChangeDetectorRef的detectChanges()钩子来手动触发当前组件的变更检测。

举例:app-icon-react动态图标组件

10.png

2.2.3 调试方式

         方式1:可以在浏览器控制台,使用Angular DevTools插件查看某一次dom事件,angular的检测情况:

11.png

         方式2:可以在控制台直接输入:ng.profiler.timeChangeDetection()查看检测时间,这种方式可查看全局的检测时间。参考博客 Profiling Angular Change Detection

12.png

2.3 模板(HTML)优化

2.3.1 减少DOM渲染:ngFor加trackBy

         使用 *ngFor 的 trackBy 属性,Angular 只更改和重新渲染已更改的条目,而不必重新加载整个条目列表。

         比如:表格排序场景。ngFor如果加了trackBy,表格渲染的时候只会移动行dom元素,如果不加trackBy,会先删除现有的表格dom元素,再新增行dom元素。显然只移动dom元素性能会好很多。

2.3.2 模板表达式中不要使用函数

         不要在Angular 模板表达式中使用函数调用,可以用管道pipe代替,也可以通过手动计算后用一个变量代替。模板中使用函数,不管值有没有改变,每次变更检测的时候都会执行函数,会影响性能。

         模板中使用函数的场景:

13.png

2.3.3 减少ngFor的使用

使用ngFor,数据量大的时候会影响性能。

举例:

使用ngFor:

14.png

15.png

不使用ngFor:性能提升10倍左右

16.png

17.png

更多编程相关知识,请访问:编程视频!!

以上就是angular如何进行性能优化?变更检测方式浅析的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/549648.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月9日 19:21:52
下一篇 2025年11月9日 19:26:45

相关推荐

  • Angular知识点分享:聊聊表单、管道、绑定、指令、通信和周期

    本篇文章给大家分享angular的一些知识点,介绍一下angular的两表单(模板驱动表单和响应式表单)、三管道(内置管道、链式管道、自定义管道)、三绑定、三指令、五通信、八周期,希望对大家有所帮助! 1 Angular的两大表单 1.1 模板驱动表单 ? 模板驱动表单:引入FormsModule模…

    2025年11月9日 web前端
    000
  • angular学习之聊聊指令和管道

    本篇文章带大家了解一下angular中的指令(directive)和管道(pipe),简单介绍一下相关知识点:内置指令和自定义指令,内置管道和自定义管道,希望对大家有所帮助! 指令 Directive 指令是 Angular 提供的操作 DOM 的途径。指令分为属性指令和结构指令。 属性指令:修改现…

    2025年11月9日 web前端
    100
  • angular学习之聊聊组件通讯和组件生命周期

    本篇文章带大家了解一下angular中的组件通讯和组件生命周期,简单介绍一下向组件内部传递数据、组件向外部传递数据的方法,希望对大家有所帮助! 组件通讯 1、向组件内部传递数据 // favorite.component.tsimport { Input } from ‘@angular/core’…

    2025年11月9日 web前端
    100
  • 什么是装饰器?聊聊Angular中怎么使用方法装饰器?

    什么是装饰器?本篇文章给大家介绍一下angular中使用方法装饰器的方法,希望对大家有所帮助! 方法装饰器并不是 Angular 的专属特性,在 es6 中也有着这种特性,本文主要是介绍 方法装饰器 在 Angular 中的使用。【相关教程推荐:《angular教程》】 什么是装饰器 在es6中,装…

    2025年11月9日 web前端
    000
  • 浅析Angular中的独立组件,看看怎么使用

    本篇文章带大家了解一下angular中的独立组件,看看怎么在angular中创建一个独立组件,怎么在独立组件中导入已有的模块,希望对大家有所帮助! Angular 14一项令人兴奋的特性就是Angular的独立组件终于来了。 在Angular 14中, 开发者可以尝试使用独立组件开发各种组件,但是值…

    2025年11月9日 web前端
    000
  • 项目过大怎么办?如何合理拆分Angular项目?

    angular项目过大,怎么合理拆分它?下面本篇文章给大家介绍一下合理拆分angular项目的方法,希望对大家有所帮助! Angular 让人诟病的一点就是打包后体积很大,一不小心 main.js就大的离谱,其实遇到类似的问题,不管是体积大、数据大、还是流量大,就一个思路:拆分。再配合浏览器的缓存机…

    2025年11月9日 web前端
    000
  • Angular中什么是结构指令?怎么使用?

    本篇文章带大家了解一下angular 中结构指令模式,介绍一下结构指令是什么且怎么使用,希望对大家有所帮助! 在 Angular 中,有两种类型的指令。属性指令修改 DOM 元素的外观或者行为。结构指令添加或者移除 DOM 元素。 结构指令是 Angular 中最强大的特性之一,然而它们却频繁被误解…

    2025年11月9日 web前端
    100
  • 聊聊自定义angular-datetime-picker格式的方法

    怎么自定义angular-datetime-picker格式?下面本篇文章聊聊自定义格式的方法,希望对大家有所帮助! 最近一直都在使用 Angular 进行开发,维护项目。遇到了日期的问题,同事采用的是 @danielmoncada/angular-datetime-picker。 PS:当然,如果…

    2025年11月9日 web前端
    000
  • 详解Angular项目中怎么给路径添加指定访问前缀

    angular项目中怎么给路径添加前缀?下面本篇文章给大家介绍一下angular项目路径添加指定的访问前缀的方法,希望对大家有所帮助! 开发多个项目的时候,我们希望能通过指定的前缀路径去访问不同的项目。比如,通过前缀 /projectA/ 去访问项目 A;通过前缀 /projectB/ 去访问项目 …

    2025年11月9日 web前端
    000

发表回复

登录后才能评论
关注微信