>  기사  >  웹 프론트엔드  >  각도 변화 감지의 DOM 업데이트 메커니즘에 대한 간략한 분석

각도 변화 감지의 DOM 업데이트 메커니즘에 대한 간략한 분석

青灯夜游
青灯夜游앞으로
2022-12-12 19:44:582486검색

각도 변화 감지의 DOM 업데이트 메커니즘에 대한 간략한 분석

변경 감지는 Angular에서 매우 중요한 부분으로, 모델과 뷰 간의 동기화를 유지하는 것입니다. 일상적인 개발 프로세스에서는 변경 감지에 대해 알 필요가 없습니다. Angular는 작업의 이 부분을 완료하는 데 도움을 주어 개발자가 비즈니스 구현에 더 집중하고 개발 효율성과 개발 경험을 향상시킬 수 있기 때문입니다. 그러나 프레임워크를 심층적으로 사용하고 싶거나 단순히 함수를 구현하는 대신 고성능 코드를 작성하려면 변경 감지를 이해해야 합니다. 이를 통해 프레임워크를 더 잘 이해하고 오류를 디버그하며 성능을 향상할 수 있습니다. [추천 관련 튜토리얼: "angular Tutorial"]

Angular의 DOM 업데이트 메커니즘

먼저 작은 예를 살펴보겠습니다.

버튼을 클릭하면 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제