>웹 프론트엔드 >JS 튜토리얼 >Angular 개발 실습(5): 변경 모니터링에 대한 심층 분석

Angular 개발 실습(5): 변경 모니터링에 대한 심층 분석

不言
不言원래의
2018-04-02 15:03:181339검색

변경 모니터링이란

Angular로 개발할 때 우리는 Angular에서 바인딩을 자주 사용합니다. 즉, 모델에서 뷰로의 입력 바인딩, 뷰에서 모델로의 출력 바인딩, 뷰와 모델 간의 양방향 바인딩입니다. 이러한 경계값이 뷰와 모델 간에 동기화될 수 있는 이유는 Angular의 변경 감지 기능 때문입니다.

간단히 말하면, 변경 감지는 Angular에서 뷰와 모델 사이에 바인딩된 값이 변경되었는지 감지하는 데 사용됩니다. 모델에 바인딩된 값이 변경되었음을 감지하면 뷰에 동기화됩니다. 그렇지 않은 경우 뷰에 바인딩된 값이 변경되면 해당 바인딩 함수가 다시 호출됩니다.

변경 모니터링의 원천

변경 모니터링의 핵심은 경계 값이 가장 작은 단위로 변경되었는지 여부를 감지하는 방법입니다. 그러면 이러한 경계 값은 어떤 상황에서 변경됩니까? 우리가 사용하는 몇 가지 일반적인 시나리오를 살펴볼 수 있습니다.

이벤트: 클릭/호버/...

@Component({
  selector: 'demo-component',
  template: `
    <h1>{{name}}</h1>
    <button (click)="changeName()">change name</button>
  `
})
export class DemoComponent {
    name: string = &#39;Tom&#39;;
    
    changeName() {
        this.name = &#39;Jerry&#39;;
    }
}

보간 표현식을 통해 템플릿의 이름 속성을 바인딩합니다. 이름 변경 버튼을 클릭하면 name 속성의 값이 변경되고, 템플릿 뷰의 표시 내용도 변경됩니다. change name按钮时,改变了 name 属性的值,这时模板视图显示内容也发生了改变。

XHR/webSocket

@Component({
  selector: &#39;demo-component&#39;,
  template: `
    <h1>{{name}}</h1>
  `
})
export class DemoComponent implements OnInit {
    name: string = &#39;Tom&#39;;
    
    constructor(public http: HttpClient) {}
    
    ngOnInit() {
        // 假设有这个./getNewName请求,返回一个新值&#39;Jerry&#39;
        this.http.get(&#39;./getNewName&#39;).subscribe((data: string) => {
            this.name = data;
        });
    }
}

我们在这个组件的 ngOnInit 函数里向服务器端发送了一个 Ajax 请求,当这个请求返回结果时,同样会改变当前模板视图上绑定的 name 属性的值。

Times: setTimeout/requestAnimationFrame

@Component({
  selector: &#39;demo-component&#39;,
  template: `
    <h1>{{name}}</h1>
  `
})
export class DemoComponent implements OnInit {
    name: string = &#39;Tom&#39;;
    
    constructor() {}
    
    ngOnInit() {
        // 假设有这个./getNewName请求,返回一个新值&#39;Jerry&#39;
        setTimeout(() => {
            this.name = &#39;Jerry&#39;;
        }, 1000);
    }
}

我们在这个组件的ngOnInit函数里通过设定一个定时任务,当定时任务执行时,同样会改变当前视图上绑定的name属性的值。

总结

  • 其实,我们不难发现上述三种情况都有一个共同点,即这些导致绑定值发生改变的事件都是异步发生的。

  • Angular并不是捕捉对象的变动,它采用的是在适当的时机去检验对象的值是否被改动,这个时机就是这些异步事件的发生。

  • 这个时机是由 NgZone 这个服务去掌控的,它获取到了整个应用的执行上下文,能够对相关的异步事件发生、完成或者异常等进行捕获,然后驱动 Angular 的变化监测机制执行。

变化监测的处理机制

通过上面的介绍,我们大致明白了变化检测是如何被触发的,那么 Angular 中的变化监测是如何执行的呢?

首先我们需要知道的是,对于每一个组件,都有一个对应的变化监测器;即每一个 Component 都对应有一个changeDetector,我们可以在 Component 中通过依赖注入来获取到changeDetector

而我们的多个 Component 是一个树状结构的组织,由于一个 Component 对应一个changeDetector,那么changeDetector

XHR/webSocket

@Component({
  selector: &#39;demo-child&#39;,
  template: `
    <h1>{{title}}</h1>
    <p>{{paramOne}}</p>
    <p>{{paramTwo}}</p>
  `
})
export class DemoChildComponent {
    title: string = &#39;子组件标题&#39;;
    @Input() paramOne: any; // 输入属性1
    @Input() paramTwo: any; // 输入属性2
}

이 구성 요소의 ngOnInit 함수에서 서버에 Ajax 요청을 보냅니다. 이 요청이 결과를 반환하면 현재 템플릿 보기에 바인딩된 이름 속성의 값도 변경됩니다.

Times: setTimeout/requestAnimationFrame

@Component({
  selector: &#39;demo-parent&#39;,
  template: `
    <h1>{{title}}</h1>
    <demo-child [paramOne]=&#39;paramOneVal&#39; [paramTwo]=&#39;paramTwoVal&#39;></demo-child>
    <button (click)="changeVal()">change name</button>
  `
})
export class DemoParentComponent {
    title: string = &#39;父组件标题&#39;;
    paramOneVal: any = &#39;传递给paramOne的数据&#39;;
    paramTwoVal: any = &#39;传递给paramTwo的数据&#39;;
    
    changeVal() {
        this.paramOneVal = &#39;改变之后的传递给paramOne的数据&#39;;
    }
}

이 컴포넌트의 ngOnInit 함수에서 예약된 작업을 설정합니다. 예약된 작업이 실행되면 현재 뷰에 바인딩된 name 속성의 값도 변경됩니다.

Summary

사실 위의 세 가지 상황에는 한 가지 공통점이 있다는 것을 찾는 것은 어렵지 않습니다. 즉, 바인딩 값을 변경시키는 이러한 이벤트가 모두 비동기적으로 발생한다는 것입니다.

Angular는 객체의 변경 사항을 캡처하지 않으며 객체의 값이 변경되었는지 확인하기 위해 적절한 타이밍을 사용합니다. 이 타이밍은 이러한 비동기 이벤트가 발생하는 시점입니다.
  • 이 타이밍은 NgZone 서비스에 의해 제어됩니다. 이는 전체 애플리케이션의 실행 컨텍스트를 획득하고 관련 비동기 이벤트의 발생, 완료 또는 예외를 캡처한 다음 Angular의 변경 모니터링 메커니즘을 실행하도록 구동합니다.
  • 변경 모니터링 처리 메커니즘
  • 위의 소개를 통해 변경 감지가 어떻게 발생하는지 대략적으로 이해했는데, Angular에서는 변경 모니터링이 어떻게 수행되나요?

    가장 먼저 알아야 할 것은 각 구성 요소에 해당하는 변경 감지기가 있다는 것입니다. 즉, 각 구성 요소는 구성 요소의 종속성 주입을 통해 얻을 수 있는 changeDetector에 해당합니다. changeDetector로.
그리고 여러 구성 요소가 트리 구조로 구성되어 있습니다. 하나의 구성 요소가 changeDetector에 해당하므로 changeDetector도 트리 구조로 구성됩니다.

마지막으로 기억해야 할 점은 모든 변경 사항 모니터링은 구성 요소 트리의 루트에서 시작된다는 것입니다.
  • 예를 들어

    Child 구성 요소:
  • @Component({
      selector: &#39;demo-parent&#39;,
      template: `
        <h1>{{title}}</h1>
      `
    })
    export class DemoParentComponent implements OnInit {
        title: string = &#39;组件标题&#39;;
        
        constructor(public cdRef: ChangeDetectorRef) {}
        
        ngOnInit() {
            this.cdRef.detach(); // 停止组件的变化监测,看需求使用不同的方法
        }
    }
  • Parent 구성 요소:

    rrreee

    위 코드에서 DemoParentComponent는 트리 구조 관점에서 5354ebd8efe1b41f1d27b438d97f8d03733dbcdb0cb52d6c005bb34feed27005 태그를 통해 DemoChildComponent를 포함합니다. DemoChildComponent의 루트 노드이고 DemoChildComponent는 DemoParentComponent의 리프 노드입니다.
  • DemoParentComponent의 버튼을 클릭하면 ChangeVal 메소드로 다시 호출된 후 변경 모니터링이 실행됩니다. 변경 모니터링 프로세스는 다음과 같습니다.

먼저 DemoParentComponent에서 변경 감지가 시작됩니다. :

제목 값이 발생했는지 감지 변경: 변경 없음

paramOneVal의 값이 변경되었는지 감지: 변경됨(changeVal() 메서드 호출 버튼을 클릭하여 변경됨)🎜🎜🎜🎜여부 감지 paramTwoVal의 값이 변경되었습니다: 변경 없음🎜🎜🎜🎜 그런 다음 변경 감지가 리프 노드로 이동합니다. DemoChildComponent: 🎜🎜🎜🎜제목 값이 변경되었는지 감지: 변경 없음🎜🎜🎜🎜paramOne이 변경되었는지 감지: 변경됨(기한 paramOneVal)🎜 🎜🎜🎜paramTwo가 변경되었는지 감지: 변경 없음🎜🎜🎜🎜마지막으로 DemoChildComponent에 더 이상 리프 노드가 없으므로 변경 모니터링은 DOM을 업데이트하고 뷰와 뷰 간의 변경 사항을 동기화합니다. 모델. 🎜🎜변경 모니터링 전략🎜🎜변경 모니터링의 처리 메커니즘을 학습한 후에는 이 메커니즘이 너무 단순하고 조잡하다고 생각할 수 있습니다. 내 애플리케이션에 수백 또는 수천 개의 구성 요소가 있고 임의의 구성 요소가 모니터링을 트리거한다고 가정해 보겠습니다. 루트 노드에서 리프 노드까지 다시 검색해야 합니다. 🎜🎜걱정하지 마세요. Angular의 개발팀은 이미 이 문제를 고려했습니다. 위의 감지 메커니즘은 단지 기본 감지 메커니즘일 뿐입니다. Angular는 OnPush 감지 메커니즘도 제공합니다(메타데이터 속성 변경 감지: ChangeDetectionStrategy.OnPush). 🎜

OnPush 与 Default 之间的差别:当检测到与子组件输入绑定的值没有发生改变时,变化检测就不会深入到子组件中去

变化监测类 - ChangeDetectorRef

上面说到我们可以修改组件元数据属性 changeDetection 来修改组件的变化监测策略(ChangeDetectionStrategy.Default 或 ChangeDetectionStrategy.OnPush),除了这个,我们还可以使用 ChangeDetectorRef 来更加灵活的控制组件的变化监测。

Angular 在整个运行期间都会为每一个组件创建 ChangeDetectorRef 的实例,该实例提供了相关方法来手动管理变化监测。有了这个类,我们自己就可以自定义组件的变化监测策略了,如停止/启用变化监测或者按指定路径变化监测等等。

相关方法如下:

  • markForCheck():把根组件到该组件之间的这条路径标记起来,通知Angular在下次触发变化监测时必须检查这条路径上的组件。

  • detach():从变化监测树中分离变化监测器,该组件的变化监测器将不再执行变化监测,除非再次手动执行reattach()方法。

  • reattach():把分离的变化监测器重新安装上,使得该组件及其子组件都能执行变化监测。

  • detectChanges():手动触发执行该组件到各个子组件的一次变化监测。

使用方法也很简单,直接在组件中注入即可:

@Component({
  selector: &#39;demo-parent&#39;,
  template: `
    <h1>{{title}}</h1>
  `
})
export class DemoParentComponent implements OnInit {
    title: string = &#39;组件标题&#39;;
    
    constructor(public cdRef: ChangeDetectorRef) {}
    
    ngOnInit() {
        this.cdRef.detach(); // 停止组件的变化监测,看需求使用不同的方法
    }
}

相关推荐:

Angular开发实践(四):组件之间的交互

Angular开发实践(二):HRM运行机制

         

위 내용은 Angular 개발 실습(5): 변경 모니터링에 대한 심층 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.