Angular 시리즈 의 변화 검출 상세 설명(Change Detection)

10702 단어 Angular변화 검출
개술
쉽게 말 하면 변화 검출 은 Angular 가 보기 와 모델 사이 에 연 결 된 값 이 바 뀌 었 는 지,모델 에 연 결 된 값 이 바 뀌 었 는 지 확인 할 때 보기 로 동기 화 하 는 것 입 니 다.반대로 보기 에 연 결 된 값 이 바 뀌 었 는 지 감지 되면 해당 하 는 바 인 딩 함 수 를 되 돌려 줍 니 다.
어떤 상황 에서 변화 검 사 를 일 으 킬 수 있 습 니까?
요약 하면 다음 과 같은 몇 가지 상황 도 데 이 터 를 바 꿀 수 있다.
사용자 입력 조작,예 를 들 어 클릭,제출 등요청 서버 데이터(XHR)시간 이벤트,예 를 들 어 setTimeout,setInterval상기 세 가지 상황 은 모두 하나의 공통점 이 있다.즉,바 인 딩 값 이 바 뀌 는 사건 은 모두 비동기 적 으로 발생 한 것 이다.만약 이러한 비동기 적 인 사건 이 발생 할 때 Angular 프레임 워 크 에 알 릴 수 있다 면 Angular 프레임 워 크 는 신속하게 변 화 를 감지 할 수 있 을 것 이다.

왼쪽 에 실행 할 코드 를 표시 합 니 다.이 stack 은 자바 script 의 실행 스 택 을 표시 합 니 다.웹 Api 는 브 라 우 저 에서 제공 하 는 자바 script 의 API 입 니 다.TaskQueue 는 자바 script 에서 작업 대기 열 을 표시 합 니 다.자바 script 은 단일 스 레 드 이 고 비동기 작업 은 작업 대기 열 에서 실 행 됩 니 다.
구체 적 으로 말 하면 비동기 실행 체 제 는 다음 과 같다.
4.567917.모든 동기 화 작업 은 주 스 레 드 에서 실행 되 고 실행 스 택(execution context stack)을 형성 합 니 다
  • 메 인 라인 외 에 도'작업 대기 열'(task quue)이 존재 합 니 다.비동기 작업 이 실 행 된 결과 만 있 으 면'작업 대기 열'에 이 벤트 를 설치 합 니 다
  • 4.567917.'실행 스 택'의 모든 동기 화 작업 이 완료 되면 시스템 은'작업 대기 열'을 읽 고 안에 어떤 사건 이 있 는 지 봅 니 다.대응 하 는 비동기 작업 은 대기 상 태 를 끝내 고 실행 창고 에 들 어가 서 실행 을 시작 합 니 다4.567917.메 인 스 레 드 는 위의 세 번 째 단 계 를 계속 반복 합 니 다상기 코드 가 자바 script 에서 실 행 될 때 먼저 func 1 이 런 스 택 에 들 어 갑 니 다.func 1 이 실 행 된 후에 setTimeout 은 런 스 택 에 들 어 갑 니 다.setTimeout 을 실행 하 는 과정 에서 리 턴 함수 cb 를 작업 대기 열 에 추가 한 다음 setTimeout 이 스 택 에 들 어 갑 니 다.이 어 func 2 함수 가 실 행 될 때 런 스 택 이 비어 있 습 니 다.이 어 작업 대기 열 에서 cb 가 런 스 택 에 들 어가 실 행 됩 니 다.비동기 작업 이 먼저 작업 대기 열 에 들 어 가 는 것 을 알 수 있 습 니 다.스 택 의 동기 화 작업 이 모두 실 행 될 때 비동기 작업 이 실행 스 택 에 들 어가 실 행 됩 니 다.이 비동기 적 인 작업 이 실행 되 기 전과 실행 후 갈고리 함 수 를 제공 할 수 있다 면 이 갈고리 함 수 를 통 해 Angular 는 비동기 적 인 작업 의 수행 을 알 수 있 습 니 다.
    angular 2 변화 알림 가 져 오기
    그렇다면 문제 가 생 겼 다.angular 2 는 데이터 가 바 뀌 었 다 는 것 을 어떻게 알 았 을 까?DOM 의 위 치 를 수정 해 야 하 는 지,정확 한 최소 범위 의 DOM 을 어떻게 알 았 을 까?맞아요.가능 한 한 작은 범위 에서 DOM 을 수정 하 세 요.왜냐하면 DOM 을 조작 하 는 것 은 성능 에 있어 서 사치품 이기 때 문 입 니 다.
    AngularJS 에 서 는 코드$scope.$apply()또는$scope.$digest 가 촉발 되 었 으 며,Angular 는 ZoneJS 에 접속 하여 Angular 의 모든 비동기 이 벤트 를 감청 하 였 습 니 다.
    존 제 이 스 는 어떻게 했 을까요?
    사실 존 에 원숭이 패 치 라 는 게 있어 요.Zone.js 가 실 행 될 때 이 비동기 이벤트 에 대한 프 록 시 패 키 지 를 만 듭 니 다.즉,Zone.js 가 실 행 된 후 setTimeout,addEventListener 등 브 라 우 저 비동기 이 벤트 를 호출 할 때 원생 을 호출 하 는 방법 이 아니 라 원숭이 패 치 에 포 장 된 프 록 시 방법 입 니 다.에이전트 에 서 는 갈고리 함수 가 설정 되 어 있 습 니 다.이 갈고리 함수 들 을 통 해 비동기 작업 이 수행 하 는 문맥 에 편리 하 게 들 어 갈 수 있 습 니 다.
    
    //   Zone.js              
    function zoneAwareAddEventListener() {...}
    function zoneAwareRemoveEventListener() {...}
    function zoneAwarePromise() {...}
    function patchTimeout() {...}
    window.prototype.addEventListener=zoneAwareAddEventListener;
    window.prototype.removeEventListener=zoneAwareRemoveEventListener;
    window.prototype.promise = zoneAwarePromise;
    window.prototype.setTimeout = patchTimeout;
    변화 검출 과정
    Angular 의 핵심 은 구성 요소 화 입 니 다.구성 요소 의 끼 워 넣 기 는 최종 적 으로 구성 요소 트 리 를 만 들 수 있 습 니 다.Angular 의 변화 검 사 는 구성 요소 로 나 누 어 진행 할 수 있 습 니 다.모든 Component 는 하나의 changeDetector 에 대응 합 니 다.우 리 는 Component 에서 의존 주입 을 통 해 changeDetector 를 얻 을 수 있 습 니 다.그리고 우리 의 여러 Component 는 나무 구조의 조직 입 니 다.하나의 Component 가 하나의 changeDetector 에 대응 하기 때문에 changeDetector 간 에 똑 같은 나무 구조의 조직 입 니 다.
    또한,Angular 의 데이터 흐름 은 부모 구성 요소 에서 하위 구성 요소 로 단 방향 으로 흐 릅 니 다.단 방향 데이터 흐름 은 효율 적 이 고 예측 가능 한 변화 검 측 을 보장 한다.부모 구성 요 소 를 검사 한 후에 도 하위 구성 요 소 는 부모 구성 요소 의 데 이 터 를 바 꾸 어 부모 구성 요 소 를 다시 검사 해 야 할 수 있 습 니 다.이것 은 추천 되 지 않 는 데이터 처리 방식 입 니 다.개발 모드 에서 Angular 는 2 차 검 사 를 진행 합 니 다.상기 상황 이 발생 하면 2 차 검 사 는 오류 가 발생 합 니 다.Expression Changed After It Has Been Checked Error.생산 환경 에 서 는 더러 운 검 사 를 한 번 만 한다.
    이에 비해 AngularJS 는 양 방향 데이터 흐름 을 사용 하고 복잡 한 데이터 흐름 으로 인해 여러 번 검 사 를 해 야 하기 때문에 데이터 가 최종 적 으로 안정 적 인 추 세 를 보인다.이론 적 으로 데 이 터 는 영원히 불안정 할 수 있다.AngularJS 는 더러 운 검 사 를 10 회 이상 하면 프로그램 에 문제 가 있다 고 보고 검 사 를 하지 않 는 다 는 전략 을 내 놓 았 다.

    변화 검출 정책
    Angular 는 두 가지 변화 검출 전략 이 있 습 니 다.Default 는 Angular 의 기본 변화 검출 정책 입 니 다.즉,위 에서 언급 한 더러 운 검사 입 니 다.값 이 바 뀌 면 모두 부모 구성 요소 에서 모든 하위 구성 요소 로 검 사 를 합 니 다.또 다른 효율 적 인 변화 검출 방식:OnPush.OnPush 정책 은 입력 데이터(즉@Input)의 인용 이 바 뀌 거나 이벤트 가 발생 했 을 때 만 구성 요소 가 변화 검 사 를 하 는 것 입 니 다.
    defalut 정책
    main.component.ts
    
    @Component({
     selector: 'app-root',
     template: `
     <h1>      </h1>
     <p>{{ slogan }}</p>
     <button type="button" (click)="changeStar()">       
     </button>
     <button type="button" (click)="changeStarObject()">
             
     </button>
     <movie [title]="title" [star]="star"></movie>`,
    })
    export class AppComponent {
     slogan: string = 'change detection';
     title: string = 'default   ';
     star: Star = new Star(' ', '  ');
     changeStar() {
      this.star.firstName = ' ';
      this.star.lastName = '  ';
     }
     changeStarObject() {
      this.star = new Star(' ', '  ');
     } 
    }
    movie.component.ts
    
    @Component({
     selector: 'movie',
     styles: ['div {border: 1px solid black}'],
     template: `
    <div>
    <h3>{{ title }}</h3>
    <p>
    <label>Star:</label>
    <span>{{star.firstName}} {{star.lastName}}</span>
    </p>
    </div>`,
    
    })
    export class MovieComponent {
     @Input() title: string;
     @Input() star;
    }
    위의 코드 에서 첫 번 째 단 추 를 누 르 면 스타 속성 을 바 꿀 때 slogan,title,star 세 가지 속성 을 차례로 검 측 합 니 다.이때 세 가지 속성 은 모두 변화 가 없습니다.star 는 변화 가 없 었 습 니 다.실질 적 으로 star 검 측 시 star 자체 의 인용 값 이 바 뀌 었 는 지 만 검 측 했 기 때 문 입 니 다.star 의 속성 값 을 바 꾸 면 star 자체 의 인용 을 바 꾸 지 않 았 습 니 다.그래서 변 함 이 없다.
    우리 가 두 번 째 단 추 를 눌 러 스타 의 대상 을 바 꾸 었 을 때 새로운 스타 가 생 겼 다.이때 변화 검 측 에서 스타 가 바 뀌 었 다 는 것 을 알 수 있다.
    그리고 변 화 는 하위 구성 요소 에 들 어가 star.firstName 과 star.lastName 에 변화 가 생 겼 음 을 감지 하고 보 기 를 업데이트 합 니 다.
    OnPush 정책
    위의 코드 에 비해 movie.coponent.ts 의@component 에 만 코드 를 추가 하 였 습 니 다.
    changeDetection:ChangeDetectionStrategy.OnPush
    이 때 첫 번 째 단 추 를 눌 렀 을 때 스타 가 변 하지 않 았 음 을 알 수 있 습 니 다.ok,변 화 는 여기 서 끝 났 습 니 다.하위 구성 요소 에 들 어가 지 않 고 보기 가 변 하지 않 습 니 다.
    두 번 째 단 추 를 누 르 면 star 에 변화 가 생 겼 음 을 감지 하고 하위 구성 요소 에 들 어 갔 을 때 star.firstName 과 star.lastName 에 변화 가 생 겼 음 을 감지 하고 보 기 를 업데이트 합 니 다.
    따라서 OnPush 검 측 체 제 를 사 용 했 을 때 바 인 딩 값 의 속성 을 수정 할 때 바 인 딩 값 자체 의 인용 을 동시에 수정 하 는 지 확인 해 야 합 니 다.하지만 속성 치 를 바 꿔 야 할 때마다 new 에 새로운 대상 으로 가 는 것 은 귀 찮 습 니 다.immutable.js 는 가 질 만 한 가치 가 있 습 니 다!
    변화 검출 대상 참조
    변화 검출 대상 Change Detector Ref 를 참조 하여 변화 검출 을 수 동 으로 조작 할 수 있 습 니 다.구성 요소 에서 주입 에 의존 하 는 방식 으로 대상 을 가 져 올 수 있 습 니 다.
    
    constructor(
      private changeRef:ChangeDetectorRef
     ){}
    변화 검출 대상 이 제공 하 는 방법 은 다음 과 같은 몇 가지 가 있다.
  • markForCheck()-구성 요소 의 metadata 에 changeDetection:ChangeDetection Strategy.OnPush 조건 이 설정 되 어 있 으 면 변경 검 사 는 다시 실행 되 지 않 습 니 다.수 동 으로 이 방법 을 호출 하지 않 는 한 이 방법 은 변경 검 측 시 이 구성 요 소 를 검사 해 야 한 다 는 뜻 입 니 다
  • detach()-변화 검출 트 리 에서 변화 검출 기 를 분리 합 니 다.이 구성 요소 의 변화 검출 기 는 reattach()방법 을 수 동 으로 호출 하지 않 는 한 변화 검출 기 를 실행 하지 않 습 니 다
  • reattach()-분 리 된 변화 감지 기 를 다시 추가 하여 이 구성 요소 와 하위 구성 요소 가 모두 변화 검 사 를 수행 할 수 있 도록 합 니 다
  • detectChanges()-이 구성 요소 에서 각 하위 구성 요소 까지 변화 검 사 를 수행 합 니 다
  • OnPush 정책 에서 수 동 으로 변화 검출
    구성 요소 에 이벤트 추가 입력 속성 변경
    위 코드 movie.coponent.ts 에서 다음 과 같이 수정 합 니 다.
    
    @Component({
     selector: 'movie',
     styles: ['div {border: 1px solid black}'],
     template: `
    <div>
    <h3>{{ title }}</h3>
    <p>
    <button (click)="changeStar()">      </button>    
    <label>Star:</label>
    <span>{{star.firstName}} {{star.lastName}}</span>
    </p>
    </div>`,
    changeDetection:ChangeDetectionStrategy.OnPush
    })
    export class MovieComponent {
     constructor(
      private changeRef:ChangeDetectorRef
     ){}
     @Input() title: string;
     @Input() star;
     
     changeStar(){
      this.star.lastName = 'xjl';
     }
    }
    
    이 때 단 추 를 누 르 면 이름 을 바 꿀 때 star 는 다음 과 같이 변 경 됩 니 다.
    ![그림 설명][3]
    두 번 째 는 위 에서 말 한 변화 검출 대상 의 markForCheck()방법 을 사용 하 는 것 이다.
    
    ngOnInit() {
      setInterval(() => {
       this.star.lastName = 'xjl';
       this.changeRef.markForCheck();
      }, 1000);
     }
    
    
    Observable 속성 입력
    app.component.ts 수정
    
    @Component({
     selector: 'app-root',
     template: `
     <h1>      </h1>
     <p>{{ slogan }}</p>
     <button type="button" (click)="changeStar()">       
     </button>
     <button type="button" (click)="changeStarObject()">
             
     </button>
     <movie [title]="title" [star]="star" [addCount]="count"></movie>`,
    })
    export class AppComponent implements OnInit{
     slogan: string = 'change detection';
     title: string = 'OnPush   ';
     star: Star = new Star(' ', '  ');
     count:Observable<any>;
    
     ngOnInit(){
      this.count = Observable.timer(0, 1000)
     }
     changeStar() {
      this.star.firstName = ' ';
      this.star.lastName = '  ';
     }
     changeStarObject() {
      this.star = new Star(' ', '  ');
     } 
    }
    
    이때 MovieComponent 를 검 측 에 들 어가 게 하 는 두 가지 방법 이 있 는데 하 나 는 변화 검 측 대상 중의 markForCheck()방법 을 사용 하 는 것 이다.
    
    ngOnInit() {
      this.addCount.subscribe(() => {
       this.count++;
       this.changeRef.markForCheck();
      })
    
    다른 하 나 는 async pipe 파 이 프 를 사용 하 는 것 입 니 다.
    
    @Component({
     selector: 'movie',
     styles: ['div {border: 1px solid black}'],
     template: `
    <div>
    <h3>{{ title }}</h3>
    <p>
    <button (click)="changeStar()">      </button>    
    <label>Star:</label>
    <span>{{star.firstName}} {{star.lastName}}</span>
    </p>
    <p>{{addCount | async}}</p>
    </div>`,
     changeDetection: ChangeDetectionStrategy.OnPush
    })
    
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기