站长家园(原代码之家)(www.adminjie.com)网站源码,微信源码,游戏源码,商业源码分享平台。
当前位置:网站首页 技术文章 网络编程 正文

浅析Angular变更检测机制,聊聊如何进行性能优化?

时间:2022-05-12 [网络编程]作者:fabuyuan 浏览:5 次

什么是变更检测?下面本篇文章带大家了解一下Angular中的变更检测机制,聊聊变更检测是如何工作的,并介绍一下Angular变更检测的性能优化方法,希望对大家有所帮助!

什么是变更检测(Change Detection)?

变更检测的概念

组件内的数据状态变化以后,需要对应更新视图。这种将视图和数据同步的机制,就叫变化检测。【相关教程推荐:《angular教程》】

变更检测的触发时机

只要发生了异步操作(Events, Timer, XHR),Angular 就会认为有状态可能发生变化了,然后就会进行变更检测。

  • Events::click,mouseover,mouseout,keyup,keydown 等浏览器事件;
  • Timer:setTimeout/setInterval;
  • XHR:各类请求等。

既然都是对异步操作进行变更检测,那么Angular是如何订阅异步请求,进行变更检测的呢?

这里介绍下NgZone以及它的fork对象Zone.js

Zone.js 用于封装和拦截浏览器中的异步活动、它还提供 异步生命周期的钩子统一的异步错误处理机制。

Zone.js 是通过 Monkey Patching(猴子补丁) 的方式来对浏览器中的常见方法和元素进行拦截,例如 setTimeoutHTMLElement.prototype.onclick。Angular 在启动时会利用 Zone.js 修补几个低级浏览器 API,从而实现异步事件的捕获,并在捕获时间后调用变更检测。

Angular通过forkZone.js并拓展出一个自己的区域NgZone,让应用中的所有异步操作都会运行在这个区域中。

Angular的变更检测如何工作的?

Angualr会为每一个组件生成一个变化监测器changeDetector ,记录组件的变化状态。

我们在创建了一个Angular 应用后,Angular 会同时创建一个 ApplicationRef 的实例,这个实例代表的就是我们当前创建的这个 Angular 应用的实例。 ApplicationRef 创建的同时,会订阅 ngZone 中的 onMicrotaskEmpty 事件,在所有的微任务完成后调用所有的视图的detectChanges()来执行变更检测。

变更检测的执行顺序

  • 更新所有子子组件绑定的属性

  • 调用所有子组件生命周期的钩子 OnChanges, OnInit, DoCheck,AfterContentInit

  • 更新当前组件的DOM

  • 调用子组件的变更检测

  • 调用所有子组件的生命周期钩子 ngAfterViewInit

举个栗子,我们在开发模式的时候可能会遇到这种报错:

这是由于变更检测遵循从根组件开始,从上到下,执行每个组件的变更检测,直到最后一个组件达到稳定状态。而在下一次变更检测之前,子孙组件都不允许去修改父组件里的属性。

情况1 在开发模式下,Angular会进行二次检测 (生产环境下调用enableProdMode(),检测次数会降为1)。一旦我们在 第4步 完成后,在子孙组件里修改父组件的属性,那么,Angular在执行第二次检测的时候,发现两次的值不一致,就会出现上述错误。

情况2 只要父组件对子组件做了属性绑定,那么不管是在OnChanges,OnInit,DoCheck,AfterContentInit 和 AfterViewInit 中的任意一个生命周期钩子中执行下述代码,也会报错。

// #parent
{{data}}
<child [data]="data"></child>

// in child component ts, execute:
this.parent.data = 'new Value';

变更检测的执行策略

  • Default 策略

    每次事件触发变化检测(如用户事件、计时器、XHR、promise 等)时,此默认策略都会从上到下检查组件树中的每个组件。这种对组件的依赖关系不做任何假设的保守检查方式称为 脏检查,这种策略在我们应用组件过多时会对我们的应用产生性能的影响。

  • OnPush 策略

    修改组件装饰器的changeDetection,设置为 OnPush 策略后,Angular 每次触发变化检测后会跳过该组件和该组件的所以子组件变化检测。

    OnPush 策略下,只有以下这几种情况才会触发组件的变化检测:

    • 输入值(@Input)更改(入input的值必须是一个新的引用)
    • 当前组件或子组件之一触发了事件 (但在onPush策略中,以下操作不会触发变更检测)
      • setTimeout()
      • setInterval()
      • Promise.resolve().then()
      • this.http.get('...').subscribe()
    • 手动触发变更检测(每个组件都会关联一个组件视图ChangeDetectorRef)
      • detectChanges(): 它会触发当前组件和子组件的变化检测
      • markForCheck(): 它不会触发变化检测,但是会把当前的OnPush组件和所以的父组件为OnPush的组件 标记为需要检测状态 ,在当前或者下一个变化检测周期进行检测
      • ApplicationRef.tick() : 它会根据组件的变化检测策略,触发整个应用程序的更改检测
    • async pipe

对于Angular的变更检测如何优化?

由于组件默认执行 Default策略 ,任何异步操作都会触发整个组件数从上到下的检查。即使Angular团队不断提升性能,可以在毫秒内完成上百次检测,但是当应用拓展至百上千个组件组成时,庞大的组件树对应的变更检测也会达到性能瓶颈。

此时,我们就需要开始分析并减少不必要的检测次数。

如何减少检测次数

  • 区域污染(Zone Pollution)

    一般我们在生命周期钩子里使用第三方库,比如chart类库初始化,会自带requestAnimationRequest/setTimeout/addEventListener,我们可以将初始化方法写入NgZonerunOutsideAngular方法中。

  • OnPush 策略

    对于不涉及更新操作的视图可以剥离出组件,使用onPush策略,以通知更新的方式刷新视图(见上方 变更检测的执行策略 部分)。

  • 用 pure pipe 代替 {{function(data)}}

    html文件内,{{function(data)}}的写法会导致每次变更检测发生时,所有数值都会重新被计算。(?:当一个1000条的列表,你只修改了其中一条数据,但另外另外999条无需更新的数据也会被重新运算。)

    此时,我们可以使用pipe的方式,只有变更的值会触发运算,更新部分视图。

  • 面对大量数据的渲染,选择虚拟滚动/分页请求数据

    以上4点解决方案,来源于Angular团队的视频介绍,视频中以Angular devtool运算次数,来分析问题解决问题。所以,如果你的Angular是9+,请继续看下去吧,如何安装Angular devtool和运行。

插件:Angular devtool使用介绍

  • Angular 9+, 支持Ivy。
  • Guide和下载地址
  • 保证运行环境为开发环境
    // environment.dev.ts
    ...
        production: false
    ...
  • angular.json > dev配置项 > "optimization": false
    projects > your-project-name > architect > build > configurations > dev > "optimization": false

更多编程相关知识,请访问:编程教学!!

以上就是浅析Angular变更检测机制,聊聊如何进行性能优化?的详细内容,更多请关注站长家园其它相关文章!

本文标签:  AngularAngular.js

转载请注明来源:浅析Angular变更检测机制,聊聊如何进行性能优化?

本文永久链接地址:https://www.adminjie.com/post/12224.html

免责声明:
本站所发布的一切资源仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

附:
二○○二年一月一日《计算机软件保护条例》第十七条规定:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬!鉴于此,也希望大家按此说明研究软件!

版权声明:
一、本站致力于为软件爱好者提供国内外软件开发技术和软件共享,着力为用户提供优资资源。
二、本站提供的部分源码下载文件为网络共享资源,请于下载后的24小时内删除。如需体验更多乐趣,还请支持正版。
三、我站提供用户下载的所有内容均转自互联网。如有内容侵犯您的版权或其他利益的,若有侵犯你的权益请:提交版权证明文件到邮箱 2225329873#qq.com(#换为@) 站长会进行审查之后,情况属实的会在三个工作日内为您删除。

  • 站长家园(原代码之家)会员升级
  • 最新文章
    • 完全掌握java之String类

      完全掌握java之String类

      本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于string类的相关问题,包括了字符串的常量池、字符串的不可变性等等相关内容,下面一起来看一下,希...

    • 简单学习Python字符和列表(实例详解)

      简单学习Python字符和列表(实例详解)

      本篇文章给大家带来了关于python的相关知识,其中主要介绍了关于字符和列表的相关问题,包括了字符串的输入输出、列表循环遍历、列表的增删改查以及列表的嵌套等等内...

    • 图文解析Redis线程模型

      图文解析Redis线程模型

      本篇文章给大家带来了关于Redis的相关知识,其中主要介绍了关于线程模型的相关问题,Redis它是一个单线程的,下面就一起来看一下,希望对大家有帮助。推荐学习:...

    • jquery怎么判断元素是否有子节点

      jquery怎么判断元素是否有子节点

      方法:1、用children()获取元素下的所有子节点,语法“元素对象.children()”,会返回一个包含子节点的jQ对象;2、用length检测子节点个数...

    • jquery对象访问的方法有哪些

      jquery对象访问的方法有哪些

      jquery对象访问的方法有:1、each(),可以遍历指定的jquery对象,语法“$.each(对象,回调函数)”;2、size(),可统计jquery对象...

    热门文章