markForCheck ()와 detectChanges ()의 차이점은 무엇입니까?


174

차이점은 무엇이며 ChangeDetectorRef.markForCheck()그리고 ChangeDetectorRef.detectChanges()?

나는 두 기능 의 차이점이 아니라에 대한 정보찾았습니다NgZone.run() .

문서에 대한 참조 만있는 답변에 대해서는 실제 시나리오 중 하나를 선택하는 방법을 설명하십시오.



@Milad 그가 그것을 하향 조정했다는 것을 어떻게 알 수 있습니까? 이 사이트를 잘 알고있는 사람들이 많이 있습니다.
Goodbye StackExchange 2018

2
@FrankerZ, 나는 글을 쓰고 있었기 때문에 downvote를 보았고 두 번째 질문은 "문서에 대한 참조 만있는 답변의 경우 하나를 선택하는 실제 시나리오를 설명해 주시겠습니까? 내 마음 속에".
Milad

3
downvote는 내가 이미 본 문서에서 복사하여 붙여 넣은 원래 답변을 완성하도록 장려했습니다. 그리고 효과가있었습니다! 이제 대답은 명확성이
국회

3
@parliament이 얼마나 끔찍한 계획입니까!
HankCa

답변:


234

문서에서 :

detectChanges () : 무효

변경 감지기와 해당 하위 항목을 확인합니다.

즉, 모델 (클래스) 내부의 항목이 변경되었지만 뷰를 반영하지 않은 경우 Angular에 변경 사항을 감지하고 (로컬 변경 감지) 뷰를 업데이트해야 할 수도 있습니다.

가능한 시나리오는 다음과 같습니다.

1- 변경 감지기가보기에서 분리되었습니다 ( 분리 참조 ).

2- 업데이트가 발생했지만 Angular 영역에 없었으므로 Angular는 이에 대해 알지 못합니다.

타사 기능이 모델을 업데이트하고 그 후에 뷰를 업데이트하려는 경우와 같습니다.

 someFunctionThatIsRunByAThirdPartyCode(){
     yourModel.text = "new text";
 }

이 코드는 Angular 영역 외부에 있기 때문에 변경 사항을 감지하고보기를 업데이트해야합니다.

 myFunction(){
   someFunctionThatIsRunByAThirdPartyCode();

   // Let's detect the changes that above function made to the model which Angular is not aware of.
    this.cd.detectChanges();
 }

참고 :

위의 작업을 수행하는 다른 방법이 있습니다. 즉, 각도 변경주기 내에 해당 변경을 가져 오는 다른 방법이 있습니다.

** zone.run 내부에서 해당 타사 기능을 래핑 할 수 있습니다.

 myFunction(){
   this.zone.run(this.someFunctionThatIsRunByAThirdPartyCode);
 }

** setTimeout 안에 함수를 래핑 할 수 있습니다.

myFunction(){
   setTimeout(this.someFunctionThatIsRunByAThirdPartyCode,0);
 }

3- change detection cycle완료 후 모델을 업데이트 하는 경우도 있습니다.이 경우이 두려운 오류가 발생합니다.

"확인 된 후 표현이 변경되었습니다";

이것은 일반적으로 (Angular2 언어에서) 의미합니다.

허용 된 방법 중 하나 (이벤트, XHR 요청, setTimeout 및 ...)로 인한 모델 변경을보고 변경 감지를 실행하여보기를 업데이트하고 완료했지만 다른 것이있었습니다. 코드에서 함수를 다시 업데이트하여 모델을 다시 업데이트했으며 AngularJS와 같은 더티 검사가 더 이상 없기 때문에 변경 감지를 다시 실행하고 싶지 않습니다. 우리는 단방향 데이터 흐름을 사용해야합니다!

이 오류가 분명히 나타납니다. : P

그것을 고치는 몇 가지 방법 :

1- 올바른 방법 : 업데이트가 변경 감지주기 내에 있는지 확인하십시오 (Angular2 업데이트는 한 번만 발생하는 한 가지 흐름이며, 그 후에 모델을 업데이트하지 말고 코드를 더 나은 장소 / 시간으로 이동하십시오).

2- 게으른 방법 : angular2를 행복하게하기 위해 업데이트 후 detectChanges ()를 실행하십시오. 이것은 가장 좋은 방법은 아니지만 가능한 시나리오가 무엇인지 물었을 때, 그중 하나입니다.

당신이 말하는 방식 : 진심으로 변경 감지를 실행했음을 알고 있지만 확인을 마친 후에 즉시 업데이트해야하기 때문에 다시 수행하고 싶습니다.

3- 영역에 의해 패치되고 완료된 후에 실행 setTimeout되므로 코드를에 넣 습니다.setTimeoutdetectChanges


문서에서

markForCheck() : void

모든 ChangeDetectionStrategy 조상을 확인할 것으로 표시합니다.

이것은 구성 요소 의 ChangeDetectionStrategyOnPush 인 경우에 주로 필요 합니다 .

OnPush 자체는 다음 중 하나가 발생한 경우에만 변경 감지를 실행 함을 의미합니다.

1- 컴포넌트의 @input 중 하나가 완전히 새로운 값으로 바뀌 었거나 @Input 속성의 참조가 완전히 바뀌었을 경우 간단히 넣습니다.

따라서 구성 요소의 ChangeDetectionStrategyOnPush 이고 다음이있는 경우

   var obj = {
     name:'Milad'
   };

그런 다음 다음과 같이 업데이트 / 변경하십시오.

  obj.name = "a new name";

이것은 obj 참조를 업데이트하지 않으므로 변경 감지가 실행되지 않으므로보기가 업데이트 / 변경을 반영하지 않습니다.

이 경우 뷰를 확인하고 업데이트하도록 수동으로 Angular에 지시해야합니다 (markForCheck).

그래서 당신이 이것을했다면 :

  obj.name = "a new name";

이 작업을 수행해야합니다.

  this.cd.markForCheck();

오히려 아래에서 변경 감지가 실행됩니다.

    obj = {
      name:"a new name"
    };

이전 obj를 완전히 새로운 것으로 대체했습니다 {}.

2- 클릭과 같은 이벤트가 발생했거나 하위 구성 요소가 이벤트를 생성했습니다.

같은 이벤트 :

  • 딸깍 하는 소리
  • 키업
  • 구독 이벤트
  • 기타

간단히 말해서 :

  • detectChanges()각도가 변경 감지를 실행 한 후 또는 업데이트가 각도 세계에 전혀없는 경우 모델을 업데이트 한 경우 사용 합니다.

  • 사용 markForCheck()당신은 인 OnPush를 사용하고 당신은을 우회하는 경우 ChangeDetectionStrategy일부 데이터를 돌연변이에 의해 또는 당신은 내부의 모델을 업데이트했습니다 에서는 setTimeout을 ;


6
따라서 obj를 변경하면보기가 업데이트되지 않으며 detectChanges를 실행하더라도 변경 사항이 없으므로 작동 하지 않습니다. 이것은 사실이 아닙니다. detectChanges업데이트보기. 이 자세한 설명을 참조하십시오 .
Max Koretskyi

결론에서 markForCheck에 관해서는 정확하지 않습니다. 여기 변형 예 에서 이 질문에 , 그것은 인 OnPush 및 markForCheck와 객체의 변화를 감지하지 않습니다. 그러나 OnPush 전략이 없으면 동일한 예제가 작동 합니다.
Estus Flask

@Maximus, 첫 번째 의견과 관련하여 게시물을 읽었습니다. 감사합니다. 그러나 설명에서 전략이 OnPush라면 this.cdMode === ChangeDetectorStatus.Checked, 뷰를 업데이트하지 않을 것이므로 markForCheck를 사용하는 것입니다.
Milad

그리고 플런저와의 링크에 관해서는 두 가지 예가 모두 잘 작동하고 있습니다. 무슨 의미인지 모르겠습니다.
Milad

@Milad, 그 의견은 @estus에서 나왔습니다 :). 내 약했다 detectChanges. 그리고 cdModeAngular 에는 없습니다 4.x.x. 나는 내 기사에 그것에 대해 씁니다. 네가 좋다 니 기쁘다. 당신이 매체에 그것을 추천하거나 나를 따라갈 수 있다는 것을 잊지 마십시오 :)
Max Koretskyi

99

이 둘의 가장 큰 차이점은 detectChanges()실제로 변경 감지를 트리거하지만 변경 감지 markForCheck()는 트리거하지 않는다는 것입니다.

detectChanges

이것은 트리거하는 구성 요소로 시작하는 구성 요소 트리에 대한 변경 감지를 실행하는 데 사용됩니다 detectChanges(). 따라서 변경 감지는 현재 구성 요소 및 모든 하위 구성 요소에 대해 실행됩니다. Angular는 루트 구성 요소 트리에 대한 참조를 보유 ApplicationRef하며 비동기 작업이 발생하면 래퍼 메서드를 통해이 루트 구성 요소에 대한 변경 감지를 트리거합니다 tick().

@Injectable()
export class ApplicationRef_ extends ApplicationRef {
  ...
  tick(): void {
    if (this._runningTick) {
      throw new Error('ApplicationRef.tick is called recursively');
    }

    const scope = ApplicationRef_._tickScope();
    try {
      this._runningTick = true;
      this._views.forEach((view) => view.detectChanges()); <------------------

view여기에 루트 컴포넌트 뷰가 있습니다. 여러 구성 요소 부트 스트랩의 의미무엇입니까? 에서 설명한 것처럼 많은 루트 구성 요소가있을 수 있습니다 .

@milad는 변경 감지를 수동으로 트리거해야하는 이유를 설명했습니다.

markForCheck

내가 말했듯이,이 사람은 변경 감지를 전혀 트리거하지 않습니다. 단순히 현재 구성 요소에서 루트 구성 요소로 올라가고 뷰 상태를로 업데이트합니다 ChecksEnabled. 소스 코드는 다음과 같습니다.

export function markParentViewsForCheck(view: ViewData) {
  let currView: ViewData|null = view;
  while (currView) {
    if (currView.def.flags & ViewFlags.OnPush) {
      currView.state |= ViewState.ChecksEnabled;  <-----------------
    }
    currView = currView.viewContainerParent || currView.parent;
  }
}

구성 요소에 대한 실제 변경 감지는 예약되지 않았지만 향후 (현재 또는 다음 CD주기의 일부로) 발생할 경우 변경 감지기를 분리 한 경우에도 상위 구성 요소보기를 검사합니다. 변경 탐지 전략 을 사용 cd.detach()하거나 지정 하여 변경 탐지기를 분리 할 수 ​​있습니다 OnPush. 모든 기본 이벤트 핸들러는 모든 상위 구성 요소보기를 확인 표시합니다.

이 접근 방식은 종종 ngDoCheck수명주기 후크 에서 사용됩니다 . 당신은 더 많은 읽을 수 있습니다 당신이 생각하는 경우 ngDoCheck구성 요소가 선택되는 방법을 -이 기사를 읽어 .

자세한 내용은 Angular의 변경 감지에 대해 알아야 할 모든 항목 을 참조하십시오.


1
구성 요소 및 상위 항목에서 markForCheck를 수행하는 동안 구성 요소 및 해당 하위 항목에서 detectChanges가 작동하는 이유는 무엇입니까?
pablo

@pablo, 의도적으로 설계된 것입니다. 나는 그 이론적 근거에 대해 잘
모른다

@ AngularInDepth.com 매우 집중적 인 처리가있는 경우 changedetecting이 UI를 차단합니까?
alt255

1
@jerry, 권장되는 접근 방식은 내부적으로 구독과 모든 새로운 값 트리거를 추적하는 비동기 파이프를 사용하는 것 markForCheck입니다. 따라서 비동기 파이프를 사용하지 않는다면 아마도 이것이 사용되어야 할 것입니다. 그러나 변경 사항 감지를 시작하기위한 일부 비동기 이벤트의 결과로 상점 업데이트가 발생해야합니다. 그것은 항상 그렇습니다. 그러나 예외가 blog.angularindepth.com/...
최대 Koretskyi

1
@MaxKoretskyiakaWizard 답장을 보내 주셔서 감사합니다. 예. 상점 업데이트는 대부분 페치 또는 isFetching 전에 설정 한 결과입니다. 이후 .. 가져하지만 우리는 항상 사용할 수 없습니다 async pipe(가) 우리가 일반적으로 같이 할 수있는 몇 가지를 구독 내부에 있기 때문에 call setFromValues do some comparison.. 그리고 만약 async자체가 호출 markForCheck우리가 자신을 호출하면 무슨 문제가? 그러나 다시 우리는 ngOnInit다른 데이터 를 얻는 데 보통 2-3 개 이상의 선택자가 markForCheck있고 모든 것을 호출 합니다. 괜찮습니까?
jerry

0

cd.detectChanges() 현재 구성 요소에서 하위 구성 요소를 통해 즉시 변경 감지를 실행합니다.

cd.markForCheck()변경 감지는 실행하지 않지만 조상은 변경 감지를 실행해야한다고 표시합니다. 다음 번에 변경 감지가 실행되면 표시된 구성 요소에 대해서도 실행됩니다.

  • 변경 감지 횟수를 줄이려면 use라고합니다 cd.markForCheck(). 종종 변경 사항이 여러 구성 요소에 영향을 미치며 변경 감지가 호출됩니다. 당신은 기본적으로 말을하는지 : 그냥 확인이 구성 요소가되어 있는지 확인하자 또한 그렇게되면 업데이트되었습니다. (보기는 내가 작성한 모든 프로젝트에서 즉시 업데이트되지만 모든 단위 테스트에서는 업데이트되지 않습니다).
  • 당신은 확인하십시오 할 수없는 경우 cd.detectChanges()되지 않고 현재 실행 변경 감지, 사용 cd.markForCheck(). detectChanges()이 경우 오류가 발생합니다. 이것은 아마도 Angular의 변경 감지가 설계되었다는 가정에 반하는 조상 구성 요소의 상태를 편집하려고 함을 의미합니다.
  • 다른 조치 전에보기를 동기식으로 업데이트해야하는 경우을 사용하십시오 detectChanges(). markForCheck()실제로 시간에 맞게보기를 업데이트하지 못할 수 있습니다. 예를 들어, 단위 테스트는보기에 영향을 미치므로 fixture.detectChanges()앱 자체에서 필요하지 않은 경우 수동으로 호출해야 할 수 있습니다 .
  • 하위 항목보다 상위 항목이 많은 구성 요소에서 상태를 detectChanges()변경하는 경우 구성 요소의 상위 항목에서 불필요하게 변경 감지를 실행하지 않기 때문에 성능을 향상시킬 수 있습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.