[javascript] 위반 오래 실행되는 JavaScript 작업에 xxms 소요

최근에 이런 종류의 경고가 나타 났으며, 이것이 처음입니다.

[Violation] Long running JavaScript task took 234ms
[Violation] Forced reflow while executing JavaScript took 45ms

나는 그룹 프로젝트를 진행 중이며 어디에서 왔는지 전혀 모른다. 이것은 전에 일어난 적이 없다. 갑자기 누군가 다른 사람이 프로젝트에 참여했을 때 나타났습니다. 이 경고의 원인이되는 파일 / 함수를 어떻게 찾습니까? 나는 답을 찾고 있었지만 대부분 그것을 해결하는 방법에 대한 해결책에 대해 이야기했습니다. 문제의 원인을 찾을 수 없으면 해결할 수 없습니다.

이 경우 경고는 Chrome에만 나타납니다. Edge를 사용하려고했지만 비슷한 경고가 표시되지 않았으며 아직 Firefox에서 테스트하지 않았습니다.

나는 심지어 오류를 얻는다 jquery.min.js:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2


답변

업데이트 : Chrome 58 이상에서는 기본적으로 이러한 디버그 메시지와 기타 디버그 메시지를 숨겼습니다. 이를 표시하려면 ‘정보’옆의 화살표를 클릭하고 ‘자세한 내용’을 선택하십시오.

Chrome 57은 기본적으로 ‘숨기기 위반’을 설정했습니다. 필터를 다시 켜려면 필터를 활성화하고 ‘위반 사항 숨기기’상자를 선택 취소해야합니다.

갑자기 다른 사람이 프로젝트에 참여했을 때 나타납니다.

Chrome 56으로 업데이트했을 가능성이 큽니다.이 경고는 훌륭한 새 기능입니다. 필자가 필사적으로 평가판을 표시하지 않을 경우에만 끄십시오. 근본적인 문제는 다른 브라우저에 있지만 브라우저는 문제가 있다고 알려주지 않습니다. Chromium 티켓이 여기 있지만 실제로 흥미로운 토론은 없습니다.

이 메시지는 실제로 큰 문제를 일으키지 않기 때문에 오류 대신 경고입니다. 프레임이 떨어지거나 덜 부드러운 느낌을 줄 수 있습니다.

그러나 응용 프로그램의 품질을 향상시키기 위해 조사하고 수정해야합니다. 이를 수행하는 방법은 메시지가 나타나는 환경에주의를 기울이고 문제가 발생한 위치를 좁히기 위해 성능 테스트를 수행하는 것입니다. 성능 테스트를 시작하는 가장 간단한 방법은 다음과 같은 코드를 삽입하는 것입니다.

function someMethodIThinkMightBeSlow() {
    const startTime = performance.now();

    // Do the normal stuff for this function

    const duration = performance.now() - startTime;
    console.log(`someMethodIThinkMightBeSlow took ${duration}ms`);
}

좀 더 고급 얻고 싶은 경우에, 당신은 또한 사용할 수있는 크롬의 프로파일 러를 , 또는 같은 벤치 마크 라이브러리를 사용하게 이 일을 .

시간이 오래 걸리는 코드를 찾은 경우 (50ms는 Chrome의 임계 값임) 몇 가지 옵션이 있습니다.

  1. 불필요 할 수있는 작업의 일부 / 모두 잘라 내기
  2. 동일한 작업을 더 빠르게 수행하는 방법 파악
  3. 코드를 여러 개의 비동기 단계로 나눕니다.

(1)과 (2)는 어렵거나 불가능할 수 있지만 때로는 정말 쉬우 며 첫 번째 시도 여야합니다. 필요한 경우 항상 할 수 있어야합니다 (3). 이를 위해 다음과 같은 것을 사용합니다.

setTimeout(functionToRunVerySoonButNotNow);

또는

// This one is not available natively in IE, but there are polyfills available.
Promise.resolve().then(functionToRunVerySoonButNotNow);

JavaScript의 비동기 특성에 대한 자세한 내용은 여기를 참조하십시오 .


답변

이들은 모두 언급했듯이 경고입니다. 그러나 이러한 문제를 해결하는 데 관심이 있다면 먼저 경고의 원인을 식별해야합니다. 강제 리플 로우 경고를받을 수있는 이유는 없습니다. 누군가가 몇 가지 가능한 옵션에 대한 목록 을 작성했습니다 . 자세한 내용은 토론을 따라갈 수 있습니다.
가능한 이유는 다음과 같습니다.

레이아웃 / 리플 로우를 강제하는 요인

JavaScript로 요청 / 불러올 때 아래의 모든 속성 또는 메서드는 브라우저가 스타일과 레이아웃을 동기식으로 계산하도록 트리거합니다 *. 이것을 리플 로우 또는 레이아웃 스 래싱 이라고도하며 일반적인 성능 병목 현상입니다.

요소

박스 메트릭

  • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight,elem.offsetParent
  • elem.clientLeft, elem.clientTop, elem.clientWidth,elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()

물건 스크롤

  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft, elem.scrollTop또한 그들을 설정

초점

  • elem.focus()이중 강제 레이아웃을 트리거 할 수 있습니다 ( source )

또한…

  • elem.computedRole, elem.computedName
  • elem.innerText( 소스 )

getComputedStyle

window.getComputedStyle()일반적으로 스타일 재 계산을 강제합니다 ( source )

window.getComputedStyle() 다음 중 하나라도 해당되면 레이아웃을 강제합니다.

  1. 요소는 그림자 트리에 있습니다
  2. 미디어 쿼리 (뷰포트 관련 쿼리)가 있습니다. 즉, 다음 중 하나 : ( 소스 ) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio,max-aspect-ratio
    • device-pixel-ratio, resolution,orientation
  3. 요청 된 속성은 다음 중 하나입니다. ( source )
    • height, width * top, right, bottom, left * margin[ -top, -right, -bottom, -left, 또는 속기 ] 마진이 고정되어있는 경우에만. * padding[ -top, -right, -bottom, -left, 또는 속기 ] 패딩이 고정되어있는 경우에만. *transform ,
      transform-origin, perspective-origin * translate, rotate,
      scale * webkit-filter, backdrop-filter * motion-path,
      motion-offset, motion-rotation * x, y, rx,ry

창문

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.getMatchedCSSRules() 힘만 스타일

양식

  • inputElem.focus()
  • inputElem.select(), textareaElem.select()( 소스 )

마우스 이벤트

  • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY
    ( )

문서

  • doc.scrollingElement 힘만 스타일

범위

  • range.getClientRects(), range.getBoundingClientRect()

SVG

편집 가능한

  • … 이미지를 클립 보드에 복사하는 것을 포함하여 많은 것들 ( source )

더 확인 여기서 하십시오 .

또한 원래 문제 의 Chromium 소스 코드 와 성능 API에 대한 토론이 있습니다. 경고 대한 설명이 있습니다.


편집 : Google PageSpeed ​​Insight 에서 레이아웃 리플 로우를 최소화하는 방법에 대한 기사도 있습니다. . 브라우저 리플 로우가 무엇인지 설명합니다.

리플 로우는 문서의 일부 또는 전부를 다시 렌더링하기 위해 문서에서 요소의 위치와 형상을 다시 계산하기위한 웹 브라우저 프로세스의 이름입니다. 리플 로우는 브라우저에서 사용자 차단 작업이므로 개발자가 리플 로우 시간을 개선하는 방법을 이해하고 리플 로우에 대한 다양한 문서 속성 (DOM 깊이, CSS 규칙 효율성, 다양한 유형 변경)의 영향을 이해하는 것이 유용합니다. 시각. 때로는 문서에서 단일 요소를 리플 로우하려면 부모 요소와 그 뒤에 오는 모든 요소를 ​​리플 로우해야 할 수 있습니다.

또한 최소화하는 방법에 대해 설명합니다.

  1. 불필요한 DOM 깊이를 줄입니다. DOM 트리에서 한 수준에서 변경하면 모든 트리 수준에서 루트까지, 수정 된 노드의 자식까지 변경 될 수 있습니다. 이로 인해 리플 로우 수행에 더 많은 시간이 소요됩니다.
  2. CSS 규칙을 최소화하고 사용하지 않는 CSS 규칙을 제거하십시오.
  3. 애니메이션과 같이 복잡한 렌더링을 변경하는 경우 흐름에서 벗어나십시오. 이를 위해 위치 절대 또는 위치 고정을 사용하십시오.
  4. 선택기 일치를 수행하는 데 더 많은 CPU 성능이 필요한 불필요한 복잡한 CSS 선택기 (특히 하위 선택기)를 피하십시오.

답변

몇 가지 아이디어 :

  • 코드의 절반을 제거하십시오.

    • 여전히 문제가 있습니까? 좋습니다, 가능성을 좁혔습니다! 반복.

    • 문제가 없습니까? 좋아, 당신이 코멘트 절반을 봐!

  • 버전 관리 시스템 (예 : Git)을 사용하고 있습니까? 그렇다면 git checkout최근 커밋 중 일부입니다. 언제 문제가 발생 했습니까? 커밋을보고 문제가 처음 도착했을 때 변경된 코드를 정확히 확인하십시오.


답변

문제의 원인을 식별하려면 애플리케이션을 실행하고 Chrome의 성능 탭에 기록하십시오 .

실행하는 데 오랜 시간이 걸리는 다양한 기능을 확인할 수 있습니다. 필자의 경우 콘솔의 경고와 관련이있는 것은 AdBlock 확장 프로그램에 의해로드 된 파일에서 가져온 것이지만 귀하의 경우에는 다른 것일 수 있습니다.

이러한 파일을 확인하고 이것이 확장 코드인지 확인하십시오. (귀하의 것이면 문제의 원인을 찾았습니다.)


답변

Chrome 콘솔의 네트워크 탭 아래에서로드하는 데 가장 오래 걸리는 스크립트를 찾으십시오.

제 경우에는 앱에 포함되었지만 아직 사용하지 않은 스크립트에 Angular 추가 세트가있었습니다.

<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-utils/0.1.1/angular-ui-utils.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-aria.min.js"></script>

“긴 실행 작업”오류가 지정한 시간보다로드하는 데 시간이 오래 걸린 유일한 JavaScript 파일입니다.

이러한 파일은 모두 다른 웹 사이트에서 오류없이 생성되었지만 거의 작동하지 않는 새 웹 앱에서이 “긴 실행 작업”오류가 발생했습니다. 제거 즉시 오류가 중지되었습니다.

가장 좋은 추측은 Angular 애드온이 시작 태그에 대해 점점 더 깊이있는 DOM 섹션을 재귀 적으로 찾고 있다는 것입니다.


답변

내 코드 에서이 메시지의 루트를 찾았습니다.이 메시지는 노드를 검색하고 숨기거나 표시했습니다 (오프라인). 이것은 내 코드였습니다.

search.addEventListener('keyup', function() {
    for (const node of nodes)
        if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
            node.classList.remove('hidden');
        else
            node.classList.add('hidden');
});

성능 탭 (프로필러)에는 약 60ms의 이벤트가 표시됩니다.
크롬 성능 프로파일 러 레이아웃 재 계산 리플 로우

지금:

search.addEventListener('keyup', function() {
    const nodesToHide = [];
    const nodesToShow = [];
    for (const node of nodes)
        if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
            nodesToShow.push(node);
        else
            nodesToHide.push(node);

    nodesToHide.forEach(node => node.classList.add('hidden'));
    nodesToShow.forEach(node => node.classList.remove('hidden'));
});

성능 탭 (프로필러)에 약 1ms의 이벤트가 표시됩니다.
크롬 프로파일 러 어두운

그리고 검색이 더 빨리 작동한다고 생각합니다 (229 노드).


답변

Apache Cordova 소스 코드에서 솔루션을 찾았습니다. 그들은 다음과 같이 구현합니다.

var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };

간단한 구현이지만 현명한 방법.

Android 4.4에서는을 사용하십시오 Promise. 구형 브라우저의 경우setTimeout()


용법:

nextTick(function() {
  // your code
});

이 트릭 코드를 삽입하면 모든 경고 메시지가 사라집니다.