Angular로 개발할 때 우리는 Angular에서 바인딩을 자주 사용합니다. 즉, 모델에서 뷰로의 입력 바인딩, 뷰에서 모델로의 출력 바인딩, 뷰와 모델 간의 양방향 바인딩입니다. 이러한 경계값이 뷰와 모델 간에 동기화될 수 있는 이유는 Angular의 변경 감지 기능 때문입니다.
간단히 말하면, 변경 감지는 Angular에서 뷰와 모델 사이에 바인딩된 값이 변경되었는지 감지하는 데 사용됩니다. 모델에 바인딩된 값이 변경되었음을 감지하면 뷰에 동기화됩니다. 그렇지 않은 경우 뷰에 바인딩된 값이 변경되면 해당 바인딩 함수가 다시 호출됩니다.
변경 모니터링의 핵심은 경계 값이 가장 작은 단위로 변경되었는지 여부를 감지하는 방법입니다. 그러면 이러한 경계 값은 어떤 상황에서 변경됩니까? 우리가 사용하는 몇 가지 일반적인 시나리오를 살펴볼 수 있습니다.
@Component({ selector: 'demo-component', template: ` <h1>{{name}}</h1> <button (click)="changeName()">change name</button> ` }) export class DemoComponent { name: string = 'Tom'; changeName() { this.name = 'Jerry'; } }
보간 표현식을 통해 템플릿의 이름 속성을 바인딩합니다. 이름 변경 버튼
을 클릭하면 name 속성의 값이 변경되고, 템플릿 뷰의 표시 내용도 변경됩니다. change name按钮
时,改变了 name 属性的值,这时模板视图显示内容也发生了改变。
@Component({ selector: 'demo-component', template: ` <h1>{{name}}</h1> ` }) export class DemoComponent implements OnInit { name: string = 'Tom'; constructor(public http: HttpClient) {} ngOnInit() { // 假设有这个./getNewName请求,返回一个新值'Jerry' this.http.get('./getNewName').subscribe((data: string) => { this.name = data; }); } }
我们在这个组件的 ngOnInit 函数里向服务器端发送了一个 Ajax 请求,当这个请求返回结果时,同样会改变当前模板视图上绑定的 name 属性的值。
@Component({ selector: 'demo-component', template: ` <h1>{{name}}</h1> ` }) export class DemoComponent implements OnInit { name: string = 'Tom'; constructor() {} ngOnInit() { // 假设有这个./getNewName请求,返回一个新值'Jerry' setTimeout(() => { this.name = 'Jerry'; }, 1000); } }
我们在这个组件的ngOnInit函数里通过设定一个定时任务,当定时任务执行时,同样会改变当前视图上绑定的name属性的值。
其实,我们不难发现上述三种情况都有一个共同点,即这些导致绑定值发生改变的事件都是异步发生的。
Angular并不是捕捉对象的变动,它采用的是在适当的时机去检验对象的值是否被改动,这个时机就是这些异步事件的发生。
这个时机是由 NgZone 这个服务去掌控的,它获取到了整个应用的执行上下文,能够对相关的异步事件发生、完成或者异常等进行捕获,然后驱动 Angular 的变化监测机制执行。
通过上面的介绍,我们大致明白了变化检测是如何被触发的,那么 Angular 中的变化监测是如何执行的呢?
首先我们需要知道的是,对于每一个组件,都有一个对应的变化监测器;即每一个 Component 都对应有一个changeDetector
,我们可以在 Component 中通过依赖注入来获取到changeDetector
。
而我们的多个 Component 是一个树状结构的组织,由于一个 Component 对应一个changeDetector
,那么changeDetector
@Component({ selector: 'demo-child', template: ` <h1>{{title}}</h1> <p>{{paramOne}}</p> <p>{{paramTwo}}</p> ` }) export class DemoChildComponent { title: string = '子组件标题'; @Input() paramOne: any; // 输入属性1 @Input() paramTwo: any; // 输入属性2 }이 구성 요소의 ngOnInit 함수에서 서버에 Ajax 요청을 보냅니다. 이 요청이 결과를 반환하면 현재 템플릿 보기에 바인딩된 이름 속성의 값도 변경됩니다. Times: setTimeout/requestAnimationFrame
@Component({ selector: 'demo-parent', template: ` <h1>{{title}}</h1> <demo-child [paramOne]='paramOneVal' [paramTwo]='paramTwoVal'></demo-child> <button (click)="changeVal()">change name</button> ` }) export class DemoParentComponent { title: string = '父组件标题'; paramOneVal: any = '传递给paramOne的数据'; paramTwoVal: any = '传递给paramTwo的数据'; changeVal() { this.paramOneVal = '改变之后的传递给paramOne的数据'; } }이 컴포넌트의 ngOnInit 함수에서 예약된 작업을 설정합니다. 예약된 작업이 실행되면 현재 뷰에 바인딩된 name 속성의 값도 변경됩니다. Summary
사실 위의 세 가지 상황에는 한 가지 공통점이 있다는 것을 찾는 것은 어렵지 않습니다. 즉, 바인딩 값을 변경시키는 이러한 이벤트가 모두 비동기적으로 발생한다는 것입니다.
Angular는 객체의 변경 사항을 캡처하지 않으며 객체의 값이 변경되었는지 확인하기 위해 적절한 타이밍을 사용합니다. 이 타이밍은 이러한 비동기 이벤트가 발생하는 시점입니다.changeDetector
에 해당합니다. changeDetector
로. changeDetector
에 해당하므로 changeDetector
도 트리 구조로 구성됩니다. 마지막으로 기억해야 할 점은 모든 변경 사항 모니터링은 구성 요소 트리의 루트에서 시작된다는 것입니다. @Component({ selector: 'demo-parent', template: ` <h1>{{title}}</h1> ` }) export class DemoParentComponent implements OnInit { title: string = '组件标题'; constructor(public cdRef: ChangeDetectorRef) {} ngOnInit() { this.cdRef.detach(); // 停止组件的变化监测,看需求使用不同的方法 } }
rrreee
위 코드에서 DemoParentComponent는 트리 구조 관점에서 5354ebd8efe1b41f1d27b438d97f8d03733dbcdb0cb52d6c005bb34feed27005 태그를 통해 DemoChildComponent를 포함합니다. DemoChildComponent의 루트 노드이고 DemoChildComponent는 DemoParentComponent의 리프 노드입니다.DemoParentComponent의 버튼을 클릭하면 ChangeVal 메소드로 다시 호출된 후 변경 모니터링이 실행됩니다. 변경 모니터링 프로세스는 다음과 같습니다.
OnPush 与 Default 之间的差别:当检测到与子组件输入绑定的值没有发生改变时,变化检测就不会深入到子组件中去。
上面说到我们可以修改组件元数据属性 changeDetection 来修改组件的变化监测策略(ChangeDetectionStrategy.Default 或 ChangeDetectionStrategy.OnPush),除了这个,我们还可以使用 ChangeDetectorRef 来更加灵活的控制组件的变化监测。
Angular 在整个运行期间都会为每一个组件创建 ChangeDetectorRef 的实例,该实例提供了相关方法来手动管理变化监测。有了这个类,我们自己就可以自定义组件的变化监测策略了,如停止/启用变化监测或者按指定路径变化监测等等。
相关方法如下:
markForCheck():把根组件到该组件之间的这条路径标记起来,通知Angular在下次触发变化监测时必须检查这条路径上的组件。
detach():从变化监测树中分离变化监测器,该组件的变化监测器将不再执行变化监测,除非再次手动执行reattach()方法。
reattach():把分离的变化监测器重新安装上,使得该组件及其子组件都能执行变化监测。
detectChanges():手动触发执行该组件到各个子组件的一次变化监测。
使用方法也很简单,直接在组件中注入即可:
@Component({ selector: 'demo-parent', template: ` <h1>{{title}}</h1> ` }) export class DemoParentComponent implements OnInit { title: string = '组件标题'; constructor(public cdRef: ChangeDetectorRef) {} ngOnInit() { this.cdRef.detach(); // 停止组件的变化监测,看需求使用不同的方法 } }
相关推荐:
위 내용은 Angular 개발 실습(5): 변경 모니터링에 대한 심층 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!