변경 감지는 Angular에서 매우 중요한 부분으로, 모델과 뷰 간의 동기화를 유지하는 것입니다. 일상적인 개발 프로세스에서는 변경 감지에 대해 알 필요가 없습니다. Angular는 작업의 이 부분을 완료하는 데 도움을 주어 개발자가 비즈니스 구현에 더 집중하고 개발 효율성과 개발 경험을 향상시킬 수 있기 때문입니다. 그러나 프레임워크를 심층적으로 사용하고 싶거나 단순히 함수를 구현하는 대신 고성능 코드를 작성하려면 변경 감지를 이해해야 합니다. 이를 통해 프레임워크를 더 잘 이해하고 오류를 디버그하며 성능을 향상할 수 있습니다. [추천 관련 튜토리얼: "angular Tutorial"]
먼저 작은 예를 살펴보겠습니다.
버튼을 클릭하면 name 속성이 변경되고 DOM이 자동으로 새 이름 값으로 업데이트됩니다.
이제 질문이 있습니다. name 값을 변경한 다음 DOM에서 innerText를 출력하면 어떤 값이 될까요?
import { Component, ViewChild, ElementRef } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ] }) export class AppComponent { name = 'Empty'; @ViewChild('textContainer') textContainer: ElementRef; normalClick(): void { this.name = 'Hello Angular'; console.log(this.textContainer.nativeElement.innerText); } }
정답하셨나요?
그렇다면 이 두 코드에서 정확히 무슨 일이 일어나는 걸까요?
네이티브 JS를 사용하여 이 코드를 작성하면 버튼을 클릭한 후에도 뷰가 확실히 변경되지 않지만 Angular에서는 뷰가 변경되는데 왜 자동으로 뷰를 업데이트합니까? 이는 zone.js라는 라이브러리와 분리될 수 없습니다. 간단히 말해서 값이 변경되는 일부 이벤트 처리를 수행합니다. 이에 대해 지금은 다음 섹션에서 자세히 설명합니다.
이 라이브러리에서 이러한 처리를 수행하지 않으려면 Angular는 zone.js를 비활성화하는 방법도 제공합니다.
main.ts에서 zone.js를 비활성화할 수 있습니다.
import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } platformBrowserDynamic().bootstrapModule(AppModule, { ngZone: 'noop' }) .catch(err => console.error(err));
zone.js를 비활성화하면 뷰가 업데이트되지 않습니다. 뷰 업데이트에 관련된 코드를 찾으려면 소스 코드로 이동하세요.
*/ class ApplicationRef { /** @internal */ constructor(_zone, _injector, _exceptionHandler, _initStatus) { this._zone = _zone; this._injector = _injector; this._exceptionHandler = _exceptionHandler; this._initStatus = _initStatus; /** @internal */ this._bootstrapListeners = []; this._views = []; this._runningTick = false; this._stable = true; this._destroyed = false; this._destroyListeners = []; /** * Get a list of component types registered to this application. * This list is populated even before the component is created. */ this.componentTypes = []; /** * Get a list of components registered to this application. */ this.components = []; this._onMicrotaskEmptySubscription = this._zone.onMicrotaskEmpty.subscribe({ next: () => { this._zone.run(() => { this.tick(); }); } }); ... } /** * Invoke this method to explicitly process change detection and its side-effects. * * In development mode, `tick()` also performs a second change detection cycle to ensure that no * further changes are detected. If additional changes are picked up during this second cycle, * bindings in the app have side-effects that cannot be resolved in a single change detection * pass. * In this case, Angular throws an error, since an Angular application can only have one change * detection pass during which all change detection must complete. */ tick() { NG_DEV_MODE && this.warnIfDestroyed(); if (this._runningTick) { const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ? 'ApplicationRef.tick is called recursively' : ''; throw new RuntimeError(101 /* RuntimeErrorCode.RECURSIVE_APPLICATION_REF_TICK */, errorMessage); } try { this._runningTick = true; for (let view of this._views) { view.detectChanges(); } if (typeof ngDevMode === 'undefined' || ngDevMode) { for (let view of this._views) { view.checkNoChanges(); } } } catch (e) { // Attention: Don't rethrow as it could cancel subscriptions to Observables! this._zone.runOutsideAngular(() => this._exceptionHandler.handleError(e)); } finally { this._runningTick = false; } } }
넓게 해석하면 이 ApplicationRef는 전체 Angular 애플리케이션의 인스턴스입니다. 생성자에서 zone(영역 라이브러리)의 onMicrotaskEmpty(이름에서 보면 마이크로 작업을 지우는 주제임)는 다음과 같습니다. 한 번 구독했습니다. 구독에서 Tick()이 호출되는데 Tick에서는 어떤 작업이 수행되나요?
생각:지난번에 생성자에서 구독하지 않는 것이 가장 좋다고 말했는데 여기서는 왜 이렇게 불규칙할까요? 물론 아닙니다. 지난번에 어떤 Angular 구성 요소를
constructor에 배치해야 하고 어떤 구성 요소를 ngOnInit에 배치해야 하는지에 대해 이야기했습니다. 하지만 여기서 ApplicationRef는 서비스이므로 초기화 코드는 constructor에만 배치할 수 있습니다. tick 함수에서 Tick 함수가 실행 중인 것으로 확인되면 이는 전체 애플리케이션의 인스턴스이고 재귀적으로 호출할 수 없기 때문에 예외가 발생합니다. 그런 다음 모든 뷰를 순회한 후 각 뷰에서
DetectChanges()를 실행합니다. 이는 변경 감지가 실행된다는 의미입니다. 변경 감지가 무엇인지는 나중에 자세히 설명하겠습니다. 그런 다음 devMode이면 모든 뷰를 다시 순회하고 각 뷰는 checkNoChanges()를 실행하여 변경 사항이 있는지 확인합니다. 변경 사항이 있으면 오류가 발생합니다(이 문제에 대해서는 나중에 자세히 설명하겠습니다. , 지금은 건너뛰세요) . 좋아, 이제 업데이트하는 방법을 알았습니다. 즉,
ApplicationRef의 tick 메서드를 호출하는 것입니다. import { Component, ViewChild, ElementRef, ApplicationRef } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
name = 'Empty';
@ViewChild('textContainer') textContainer: ElementRef = {} as any;
constructor(private app: ApplicationRef){}
normalClick(): void {
this.name = 'Hello Angular';
console.log(this.textContainer.nativeElement.innerText);
this.app.tick();
}
}
물론, 뷰는 정상적으로 업데이트될 수 있습니다.
DOM 업데이트는
tick()트리거에 의존합니다. zone.js는 개발자가 이 작업을 수동으로 트리거하는 것을 방지합니다. 좋습니다. 이제 zone.js를 활성화할 수 있습니다. 그럼 변화 감지란 무엇인가요? 다음 기사를 계속 기대해 주세요.
더 많은 프로그래밍 관련 지식을 보려면
프로그래밍 교육위 내용은 각도 변화 감지의 DOM 업데이트 메커니즘에 대한 간략한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!