zoukankan      html  css  js  c++  java
  • angular11源码探索二十四[路由检测变更策略]

    NgZone

    官网文档

    https://angular.io/guide/zone

    Angular为我们提供了NgZone服务,对于一些频繁的操作,可以不去触发变更检测。工作时优化性能

    变更检测

    • Events - 一些事件,例如 clickchangeinputsubmit 等;
    • XMLHttpRequests - 网络请求;
    • Timers - setTimeout()setInterval() API 等;

    每次变更检测都意味着额外的计算和资源消耗,如果我们需要对应用进行性能优化,那么首先该从这个概念下手。Angular 引入 Zone.js 以处理变更检测,具体来说,Zone.js 通过对所有常见的异步 API 打上了“补丁” 以追踪所有的异步操作,进而使 Angular 可以决定何时刷新 UI。

    Zone.js

    • Zone 是一种用于拦截和跟踪异步工作的机制。
    • 在 Angular 应用中,每个 task 都会在 “Angular” Zone 中运行,这个 Zone 被称为 NgZone。一个 Angular 应用中只存在一个 Angular Zone,而变更检测只会由 运行于这个 NgZone 中的异步操作触发。

    示例

    函数runOutsideAngular 用于确保代码中在NgZone之外运行,保证Angular的变更检测不会因为相关代码而触发

    setInterval 定时器便不会触发变更检测

    export class OneComponent implements OnInit {
      constructor(
        private ngZone: NgZone
      ) {
        this.ngZone.runOutsideAngular(() => {
          setInterval(() => {
            ++this.num;
          }, 1000);
        });
      }
    
      num = 1;
    }    
    

    我们发现因为变更检测没有出发,所以视图没有更新

    <h1>{{num}}</h1>
    

    run 方法的目的与 runOutsideAngular 正好相反:任何写在 run 里的方法,都会进入 Angular Zone 的管辖范围

      this.ngZone.runOutsideAngular(() => {
          const token = setInterval(() => {
            this.ngZone.run(() => {
              ++this.num;
            });
            if (this.num == 10) {
              clearInterval(token);
            }
          }, 1000);
        });
      num = 1;
    

    我们发现页面更新啦,

    其实这是我在源码单元测试看到的,疑惑查了下,感觉在写大屏的时候可以使用的

    如果我们有一个一定时间后更新一个值

     this.ngZone.runOutsideAngular(() => {
          const token = setTimeout(() => {
            this.ngZone.run(() => {
              this.num = 10;
            });
            clearTimeout(token);
          }, 5000);
        });
    

    ApplicationRef

    tick

    调用此方法可显式处理更改检测及其副作用。

    export class OneComponent implements OnInit {
    
      constructor(
        private ngZone: NgZone,
        private app: ApplicationRef
      ) {
        this.ngZone.runOutsideAngular(() => {
          const token = setTimeout(() => {
            this.num = 100;
              //调用更新检测
            app.tick();
            clearTimeout(token);
          }, 3000);
        });
      }
    }
    

    我们发现上面的案例更加适用于NgZone

    那我们可不可以从脏依赖的角度上来看

    @Component({
      selector: "hello",
      template: `
        <h1>Hello {{ name }}!</h1>
        <p>{{ counter }}</p>
        <button (click)="click()">click</button>
      `,
      changeDetection: ChangeDetectionStrategy.OnPush
    })
    export class HelloComponent {
      counter: number = 0;
      @Input() name: string;
      constructor(private app: ApplicationRef) {
        setInterval(() => this.counter++, 1000);
      }
      click() {
        this.app.tick();
      }
    }
    
    

    markForCheck()、OnPush、zone.js与detectChanges()

    Angular 更新 HTML 的原理

    • 初始化 Component 。在最开始 bootstrapping Angular Application 的时候,Angular 会载入 bootstrap component 并调用 ApplicationRef.tick() 方法来触发 变更策略检测
    • 监听事件,如果DOM的事件更新了 Angular component 里面的data, 那么 变更策略检测 触发
    • HTTP 数据请求。如果在 Angular component 里更新了请求的数据
    • 宏任务微任务 ,方法里更新了 Angular component 的数据。比如 setTimeout(), setInterval()Promise.then() 里更新了 Angular component 的数据

    zone.js

    Angular 自带了 ngZone 的 service,这个 service 会创建一个叫 ‘angular’ 的 Zone。在这个 Angular Zone 里,当没有 schedule MicroTasks 或是当执行 sync/async function 的时候会自动触发 change detection。如果 Angular 的检测策略是的 default 的,那么所有的 asynchronous operations 都是在 Angular Zone 里,并且都会按以上条件自动触发 change detection。

    不触发

    this.ngZone.runOutsideAngular(() => {
          不触发
          });
    this.ngZone.run(() => {
          触发
          });
    

    detectChanges()

    当这个第三方 APIs 的方法 update data 的时候,这个 update 是在 Angular Zone 之外的,所以 Angular 并不会知道这个 update 的发生,因此也不会触发变更策略检测。在这个情况下,就可以调用 ChangeDetectorRef.detectChanges() 来手动触发变更策略检测 也可以放在zone.run() 方法

    关于 OnPushmarkForCheck()

    如果 Angular 的检测策略被设置为 OnPush,那么只有以下条件下 change detection 才会被触发:

    • 当带有 @Input property 的引用完全变化或是完全被新的值替换的时候。
    • 当当前 component 或是 child component 触发 event 的时候,例如 Click,KeyUp,Subscription 等等。

    很多时候 update 就不会触发变更策略检测。这时就必须通过调用 markForCheck() 来手动告诉 Angular 去检查并更新 view。所以总体来说,markForCheck() 基本上只在 Angular 的检测策略被设置为 OnPush 的时候会被需要使用到。

    markForCheck()detectChanges() 的核心区别

    最后,总结一下 markForCheck()detectChanges() 的核心区别。detectChanges() 会真正触发 Angular 的 change detection,而 markForCheck() 则不会。

    当我们禁用NoopZone

    Zone帮助Angular知道何时触发更改检测,并使开发人员专注于应用程序开发。默认情况下,Zone已加载且无需额外配置即可工作。但是,您不必一定要使用ZoneAngular。相反,您可以选择自己触发更改检测。

    要删除Zone.js,请进行以下更改。

    1. zone.js从中删除导入polyfills.ts

      // import 'zone.js/dist/zone';  // Included with Angular CLI.`
      
    2. Bootstrap Angular的noop区域位于src/main.ts

      platformBrowserDynamic().bootstrapModule(AppModule, { ngZone: 'noop' })
        .catch(err => console.error(err));
      

    思考

    删除Zone.js 让我们获得更多性能优势,如果一切都是ChangeDetectionStrategy.OnPush

    则永远不会触发更改检测周期,除非通过下面

    • ChangeDetectorRef.detectChanges()
    • ApplicationRef.tick()

    注意一个易错点ChangeDetectorRef.markForCheck,它仅设置组件的视图层,而不会触发更改检测周期

    决定自己的高度的是你的态度,而不是你的才能

    记得我们是终身初学者和学习者

    总有一天我也能成为大佬

  • 相关阅读:
    如何开发优秀的HTML5游戏?迪斯尼《寻找奥兹之路》游戏技术详解(一)
    C++ Prime学习过程中的细节摘记(三)
    android学习笔记53_采用网页设计软件界面,以及使用android系统内置的浏览器,利用js调用java方法
    黑马韩前成linux从入门到精通の轻松搞定负载均衡
    thinkphp一键清除缓存的方法
    使用treeNMS管理及监控Redis
    ThinkPHP 的缓存大概多久更新一次
    redis查数据
    redis可视化客户端工具TreeNMS
    MeeGo开发进程通信核心 DBus调试工具 狼人:
  • 原文地址:https://www.cnblogs.com/fangdongdemao/p/14306364.html
Copyright © 2011-2022 走看看