>웹 프론트엔드 >JS 튜토리얼 >브라우저 렌더링 성능 최적화

브라우저 렌더링 성능 최적화

巴扎黑
巴扎黑원래의
2017-06-26 11:53:491896검색

렌더링 성능

페이지는 빠르게 로드될 뿐만 아니라 스크롤이 손가락으로 스와이프하는 것만큼 빨라야 하며 애니메이션과 상호 작용은 실크처럼 매끄러워야 합니다.

60fps 대 장치 새로 고침 빈도

현재 대부분의 장치의 화면 새로 고침 빈도는 60 /입니다. 따라서 페이지에 애니메이션 또는 그라데이션 효과가 있거나 사용자가 페이지를 스크롤하는 경우 브라우저가 애니메이션 또는 페이지의 각 프레임을 렌더링하는 속도는 장치 화면의 새로 고침 빈도와 일치해야 합니다.

각 프레임에 할당된 시간은 16밀리초(1초/60 = 16.66밀리초)가 조금 넘습니다. 하지만 실제로는 브라우저에서 해야 할 정리 작업이 있으므로 모든 작업은 10 ms 이내에 완료되어야 합니다. 이 예산을 충족할 수 없으면 프레임 속도가 떨어지고 콘텐츠가 화면에 떨리게 됩니다. 이 현상을 흔히 jank(jank)이라고 부르며 사용자 경험에 부정적인 영향을 미칩니다.

픽셀 파이프라인

작업할 때 알고 주의해야 할 5가지 주요 영역이 있습니다. 이 부분은 가장 많이 제어할 수 있는 부분이자 픽셀-화면 파이프라인의 핵심 포인트입니다.

  • JavaScript. 일반적으로 JavaScript는 시각적인 변화를 달성하는 데 사용됩니다. 예를 들어 jQuery의 애니메이션 기능을 사용하여 애니메이션을 생성하거나, 데이터 세트를 정렬하거나, 일부 DOM 요소를 페이지에 추가할 수 있습니다. JavaScript 외에도 CSS 애니메이션, 전환 및 웹 애니메이션 API와 같은 시각적 변경을 수행하는 다른 일반적인 방법이 있습니다.

  • 스타일 계산스타일 계산. 이는 일치하는 선택기(예: .headline 또는 .nav > .nav__item)를 기반으로 어떤 CSS 규칙이 어떤 요소에 적용되는지 파악하는 프로세스입니다. 규칙이 알려지면 각 요소에 대한 최종 스타일이 적용됩니다. 계산됩니다.

  • layout. 요소에 어떤 규칙이 적용되는지 파악한 후 브라우저는 해당 요소가 차지할 공간과 화면에서의 위치를 ​​계산하기 시작할 수 있습니다. 웹 페이지의 레이아웃 모드는 한 요소가 다른 요소에 영향을 미칠 수 있음을 의미합니다. 예를 들어, 요소의 너비는 일반적으로 트리의 모든 하위 요소와 노드의 너비에 영향을 미칩니다. 프로세스가 자주 발생합니다.

  • 그리기. 드로잉은 픽셀을 채우는 과정입니다. 여기에는 요소의 보이는 모든 부분을 포함하여 텍스트, 색상, 이미지, 테두리 및 그림자를 그리는 작업이 포함됩니다. 그리기는 일반적으로 여러 표면(레이어라고도 함)에서 수행됩니다.

  • 합성. 페이지의 일부는 여러 레이어에 그려질 수 있으므로 페이지를 올바르게 렌더링하려면 올바른 순서로 화면에 그려야 합니다. 실수로 인해 한 요소가 다른 요소 위에 잘못 나타날 수 있으므로 이는 다른 요소와 겹치는 요소의 경우 특히 중요합니다.

파이프라인의 모든 부분에는 결함이 발생할 기회가 있으므로 코드가 파이프라인의 어느 부분을 트리거하는지 정확히 아는 것이 중요합니다.

모든 프레임이 항상 파이프라인의 모든 부분을 통과하는 것은 아닙니다. 실제로 JavaScript, CSS 또는 웹 애니메이션을 사용하는 경우에는 일반적으로 지정된 프레임에 대해 파이프라인을 실행하는 세 가지 방법이 있습니다.

1. JS / CSS > ; 합성

요소의 "레이아웃" 속성을 수정하는 경우, 즉 요소의 기하학적 속성(예: 너비, 높이 등)을 변경하면 브라우저가 다른 모든 요소를 ​​확인해야 합니다. 그런 다음 페이지를 "자동으로 리플로우"합니다(페이지 리플로우). 영향을 받은 모든 부품을 다시 칠해야 하며 최종 칠한 요소를 합성해야 합니다.

2. JS/CSS > 스타일 > 그리기 > 합성

배경 이미지, 텍스트 색상, 그림자 등의 속성을 수정하는 경우 페이지 레이아웃에 영향을 주지 않으면 브라우저는 레이아웃을 건너뛰지만 여전히 그리기를 수행합니다.

3. JS / CSS > 스타일 > 구성

릴레이아웃이나 다시 그리기가 필요하지 않은 속성을 변경하면 브라우저는 구성만 수행합니다. 이 마지막 접근 방식은 오버헤드가 가장 적으며 애니메이션이나 스크롤링과 같은 애플리케이션 수명 주기의 스트레스가 높은 지점에 가장 적합합니다.

성능은 실행을 피하고 수행하는 모든 작업을 최대한 효율적으로 만드는 기술입니다. 많은 경우 이를 위해서는 브라우저를 상대로 작업하는 것이 아니라 브라우저를 사용하여 작업해야 합니다. 위에 나열된 다양한 파이프라인 작업은 계산 오버헤드가 다양하며 일부 작업은 다른 작업보다 비용이 더 많이 든다는 점을 명심할 가치가 있습니다!

JavaScript 실행 최적화

JavaScript는 종종 스타일 조작을 통해 직접적으로 발생하기도 하고, 때로는 데이터 검색이나 정렬과 같은 시각적 변화를 초래하는 계산으로 인해 JavaScript가 성능 문제의 원인이 될 수도 있습니다. .공동의 원인과 노력을 통해 영향을 최소화해야 합니다.

JavaScript 성능 분석은 작성된 JavaScript 코드가 실제 실행되는 코드와 완전히 다르기 때문에 예술이라고 할 수 있습니다. 최신 브라우저는 JIT 컴파일러와 다양한 최적화 및 트릭을 사용하여 가능한 가장 빠른 실행을 달성하며, 이는 코드의 동적 특성을 크게 변경합니다.

애플리케이션이 JavaScript를 잘 수행하는 데 도움이 되는 몇 가지 사항:

  • 애니메이션 효과의 경우 setTimeout 또는 setInterval을 사용하지 말고 requestAnimationFrame을 사용하세요.

  • 장기 실행 JavaScript를 메인 스레드에서 Web Worker로 이동하세요.

  • 작은 작업을 사용하여 여러 프레임에 걸쳐 DOM 변경을 수행하세요.

  • Chrome DevTools의 타임라인과 JavaScript 프로파일러를 사용하여 JavaScript의 영향을 평가하세요.

requestAnimationFrame을 사용하여 시각적 변화 달성

화면에서 시각적 변화가 발생하는 경우 프레임 시작 부분에서 작업을 수행하는 것이 가장 좋습니다. 프레임 시작 시 JavaScript가 실행되도록 보장하는 유일한 방법은 requestAnimationFrame을 사용하는 것입니다.

/**
 * If run as a requestAnimationFrame callback, this
 * will be run at the start of the frame. */function updateScreen(time) {  // Make visual updates here.}
requestAnimationFrame(updateScreen);

Frameworks 또는 예제는 애니메이션과 같은 시각적 변경을 수행하기 위해 setTimeout 또는 setInterval을 사용할 수 있지만 이 접근 방식의 문제점은 콜백 함수가 프레임의 특정 지점, 아마도 프레임의 끝에서 바로 실행된다는 것입니다. 이로 인해 종종 프레임이 떨어지고 끊김 현상이 발생했습니다. (복합과 같은 js를 실행하는 데 시간이 걸리고 UI 업데이트가 차단됩니다.)

실제로 jQuery의 현재 기본 애니메이션 동작은 setTimeout을 사용하는 것입니다. requestAnimationFrame을 사용하도록 패치하는 것이 좋습니다.

복잡성을 줄이거나 웹 워커를 사용하세요

JavaScript는 스타일 계산, 레이아웃 및 대부분의 경우 그리기와 함께 브라우저의 메인 스레드에서 실행됩니다. JavaScript가 너무 오랫동안 실행되면 다른 작업이 차단되어 잠재적으로 프레임이 삭제될 수 있습니다.

그러므로 JavaScript가 실행되는 시기와 기간에 대해 현명하게 생각하세요. 예를 들어 스크롤과 같은 애니메이션을 수행하는 경우 JavaScript를 3-4 ms 범위 내로 유지하는 방법을 찾는 것이 가장 좋습니다. 그 이상은 시간이 너무 많이 걸릴 수 있습니다. 자유롭다면 시간에 대해 그렇게 걱정할 필요가 없습니다.

많은 경우 순수한 계산 작업은 웹 작업자로 이동할 수 있습니다. 예를 들어 DOM 액세스, 데이터 조작 또는 순회(예: 정렬 또는 검색)가 필요하지 않은 작업은 로드 및 모델 생성과 마찬가지로 이 모델에 잘 맞는 경우가 많습니다. .

var dataSortWorker = new Worker("sort-worker.js?1.1.11");
dataSortWorker.postMesssage(dataToSort);// The main thread is now free to continue working on other things...dataSortWorker.addEventListener('message', function(evt) {   var sortedData = evt.data;   // Update data on screen...});

모든 작업이 이 모델에 적합한 것은 아닙니다. 웹 작업자는 DOM 액세스 권한이 없습니다. 작업을 기본 스레드에서 수행해야 하는 경우 대규모 작업을 더 작은 작업으로 분할하는 일괄 접근 방식을 고려하세요. 각 작업은 몇 밀리초도 걸리지 않고 각 프레임에 대한 requestAnimationFrame 핸들러 내에서 실행됩니다.

var taskList = breakBigTaskIntoMicroTasks(monsterTaskList);
requestAnimationFrame(processTaskList);function processTaskList(taskStartTime) {  var taskFinishTime;  do {// Assume the next task is pushed onto a stack.var nextTask = taskList.pop();// Process nextTask.    processTask(nextTask);// Go again if there’s enough time to do the next task.taskFinishTime = window.performance.now();
  } while (taskFinishTime - taskStartTime < 3);  if (taskList.length > 0)
    requestAnimationFrame(processTaskList);
}

이 접근 방식은 UX 및 UI에 영향을 미치므로 사용자가 작업이 처리되고 있음을 알 수 있도록 진행률 또는 활동 표시기를 사용해야 합니다. 어떤 상황에서도 이 방법은 애플리케이션의 메인 스레드를 묶지 않으므로 메인 스레드가 항상 사용자 상호 작용에 빠르게 응답하도록 돕습니다.

JavaScript의 "프레임 세금"을 이해하세요

프레임워크, 라이브러리 또는 자체 코드를 평가할 때 JavaScript 코드를 프레임별로 실행하는 오버헤드를 평가해야 합니다. 이는 변환이나 스크롤과 같이 성능이 중요한 애니메이션 작업을 수행할 때 특히 중요합니다.

JavaScript 오버헤드와 성능을 측정하는 가장 좋은 방법은 Chrome DevTools를 사용하는 것입니다. 일반적으로 다음과 같은 간단한 기록을 얻게 됩니다.

기본 섹션에서는 JavaScript 호출에 대한 플레임 차트를 제공하므로 어떤 함수가 호출되었는지, 각 함수에 소요된 시간을 정확하게 분석할 수 있습니다. -JavaScript를 실행하면 DevTools 사용자 인터페이스 상단에서 JavaScript 분석기를 활성화할 수 있습니다.

이러한 방식으로 JavaScript를 분석하면 오버헤드가 발생하므로 JavaScript에 대해 더 깊이 이해하려는 경우에만 활성화하십시오. 런타임 기능. 이 확인란을 활성화하면 이제 동일한 작업을 수행할 수 있으며 JavaScript에서 어떤 기능이 호출되는지에 대한 자세한 정보를 얻을 수 있습니다.


이 정보를 사용하여 JavaScript가 애플리케이션 성능에 미치는 영향을 평가하고 식별을 시작할 수 있습니다. 기능 실행에 너무 오랜 시간이 걸리는 핫스팟을 수정합니다. 앞서 언급했듯이 장기 실행 JavaScript를 제거해야 하며, 이것이 가능하지 않은 경우 이를 Web Worker로 이동하여 다른 작업을 계속 수행할 수 있도록 기본 스레드를 확보해야 합니다.

JavaScript의 미세 최적화를 피하세요

요소의 offsetTop을 요청하는 것이 getBoundingClientRect()를 계산하는 것보다 빠르다는 것과 같이 브라우저가 함수의 한 버전을 다른 버전보다 100배 빠르게 실행한다는 사실을 아는 것이 좋을 수도 있습니다. 매 프레임마다 호출하고 있습니다. 이러한 기능의 정도는 거의 항상 작습니다. 따라서 JavaScript 성능의 이러한 측면에 초점을 맞추는 것은 종종 노력을 낭비하는 것입니다. 일반적으로 10분의 1초 정도만 절약됩니다.

게임이나 계산 비용이 많이 드는 응용 프로그램을 개발하는 경우 일반적으로 단일 프레임에 많은 계산을 수행하게 되며 이 경우 다양한 방법이 유용하므로 이 가이드에서 예외가 될 수 있습니다.

간단히 말하면, 미세 최적화는 빌드 중인 앱 유형에 매핑되지 않는 경우가 많으므로 주의해서 사용하세요. 2/8 규칙에 따라 병목 현상이 있는 부분부터 먼저 최적화를 시작하세요.

스타일 계산의 범위와 복잡성을 좁힙니다.

요소 추가 및 제거, 속성, 클래스 변경 또는 애니메이션을 통해 DOM을 변경하면 브라우저가 요소 스타일을 다시 계산하게 되고 많은 경우 페이지도 변경됩니다. 또는 레이아웃할 페이지의 일부(즉, 자동으로 리플로우됨) 이 프로세스를 계산된 스타일이라고 합니다. 계산.

스타일 계산의 첫 번째 부분은 일치하는 선택기 세트를 만드는 것입니다. 이는 기본적으로 주어진 요소에 적용할 클래스, 의사 선택기 및 ID를 파악하는 브라우저입니다.

두 번째 부분에서는 일치하는 선택기에서 모든 스타일 규칙을 가져오고 이 요소의 최종 스타일을 파악하는 작업이 포함됩니다. Blink(Chrome 및 Opera용 렌더링 엔진)에서 이러한 프로세스의 오버헤드는 적어도 현재로서는 대략 동일합니다.

대략 50%의 시간을 사용했습니다. 요소에 대한 계산된 스타일을 계산하는 것은 선택기를 일치시키는 데 사용되며, 나머지 절반은 일치하는 규칙에서 RenderStyle(계산된 스타일 표현)을 만드는 데 사용됩니다.

    선택기 복잡성을 줄입니다. BEM 사양(Block-Element_Modifer)과 같은 클래스 중심 접근 방식을 사용합니다.
  • 스타일을 계산해야 하는 요소 수를 줄입니다.
선택기 복잡성 줄이기

가장 간단한 경우 CSS에는 단 하나의 요소 클래스만 있습니다.

.title {
/* 스타일 */
}

그러나 프로젝트가 성장함에 따라 더 복잡한 CSS를 생성할 수 있으며 결국에는 선택기는 다음과 같이 될 수 있습니다:

.box:nth-last-child(-n+1) .title {
/* 스타일 */
}

스타일을 적용해야 하는지 알기 위해 브라우저는 실제로 "이것은 부모가 정확히 음의 N번째 하위이고 상자 클래스가 있는 1개의 요소인 제목 클래스가 있는 요소입니까?"라고 질문해야 합니다. " "이 결과를 계산하는 데는 사용된 선택기와 해당 브라우저에 따라 상당한 시간이 걸릴 수 있습니다. 특정 선택기를 클래스로 변경할 수 있습니다:

.final-box-title {
/* 스타일 */
}

개발자는 클래스 이름에 문제가 있을 수 있지만 브라우저에서는 작업이 훨씬 간단합니다. 이전 버전에서는 요소가 해당 유형의 마지막 요소인지 확인하기 위해 브라우저는 먼저 다른 요소에 대한 모든 정보와 그 뒤에 오는 요소가 N번째 마지막 하위 요소인지 여부를 알아야 했습니다. 이는 단순히 일치하는 것보다 더 빨랐습니다. 요소에 대한 클래스 선택기는 훨씬 더 비쌉니다.

렌더 트리를 생성할 때 각 DOM 요소에 대해 일치하는 선택기를 모든 스타일 규칙에서 찾아야 하며 해당 스타일 규칙을 병합해야 합니다.
CSS 선택기는 오른쪽에서 왼쪽으로 구문 분석되므로 공통 스타일은 CSSOM 트리의 상위 노드에 있고 더 구체적인 스타일(선택기가 더 구체적임)은 하위 노드에 있고 노드 수는 분기 및 순회가 줄어듭니다. CSS 규칙을 읽기 위해 왼쪽에서 오른쪽 방법을 사용하면 대부분의 규칙이 끝까지 일치하지 않는 것으로 발견되고 오른쪽에서 왼쪽 방법을 사용하는 동안 많은 쓸모없는 작업이 수행됩니다. 가장 오른쪽 선택기가 일치하지 않는 한, 일치하는 항목이 있으면 직접 삭제하여 잘못된 일치 항목이 많이 발생하지 않도록 하세요.

스타일이 계산되는 요소 수 줄이기

또 다른 성능 고려 사항으로, 요소가 변경될 때 계산해야 하는 작업량은 많은 스타일 업데이트에서 더 중요한 요소입니다.

일반적으로 계산된 스타일을 계산하는 데 드는 최악의 비용은 elements는 요소 수에 선택기 수를 곱한 값입니다. 왜냐하면 각 요소를 확인하려면 모든 스타일 규칙에 대해 한 번 이상 확인해야 합니다. 일치하는 경우.

참고: 예전에는 다음과 같았습니다. 예를 들어 body 요소의 클래스를 변경한 경우 페이지의 모든 하위 요소는 계산된 스타일을 다시 계산해야 합니다. 이제는 조금 다릅니다. 변경 시 스타일이 다시 계산되도록 하는 요소의 경우 일부 브라우저는 각 요소에 고유한 작은 규칙 집합을 유지합니다. 이는 트리에서의 요소 위치와 변경된 특정 속성에 따라 요소를 반드시 다시 계산할 필요가 없음을 의미합니다.

스타일 계산은 전체 페이지를 무효화하는 대신 소수의 대상 요소를 대상으로 하는 경우가 많습니다. 최신 브라우저에서는 브라우저가 변경 사항이 영향을 미칠 수 있는 모든 요소를 ​​반드시 확인할 필요가 없기 때문에 이는 문제가 되지 않는 경우가 많습니다. 반면에 오래된 브라우저는 반드시 이러한 작업에 최적화되어 있는 것은 아닙니다. 유효하지 않은 것으로 선언된 요소의 수는 가능한 한 줄여야 합니다.

참고: 웹 구성 요소를 사용하는 경우 스타일 계산이 약간 다르다는 점에 주목할 가치가 있습니다. 기본적으로 스타일은 Shadow DOM의 경계를 넘지 않고 전체 트리가 아닌 단일 구성 요소로 범위가 지정되기 때문입니다. . 그러나 전반적으로 동일한 개념이 여전히 적용됩니다. 간단한 규칙이 있는 작은 트리는 더 복잡한 규칙이 있는 큰 트리보다 더 효율적으로 처리됩니다.

스타일 재계산 비용 측정

스타일 재계산을 측정하는 가장 쉽고 좋은 방법은 Chrome DevTools의 타임라인 모드를 사용하는 것입니다. 먼저 DevTools를 열고 타임라인 탭으로 이동하여 녹음을 선택한 후 사이트와 상호작용하세요. 녹화를 중지하면 아래 그림과 같은 상황을 볼 수 있습니다.

상단의 막대는 초당 프레임 수를 나타내며, 막대가 아래쪽 선, 즉 60fps 선을 초과하는 경우 프레임이 오래 실행되는 것입니다.


스크롤이나 기타 상호 작용과 같은 일부 상호 작용에 장시간 실행 프레임이 있는 경우 추가로 검토해야 합니다.

위 예시처럼 더 큰 보라색 블록이 나타나면 녹화를 클릭해 자세한 내용을 알아보세요.

이 크롤링에는 18밀리초가 조금 넘는 시간이 소요되고 스크롤 중에 발생하여 사용자에게 눈에 띄는 불안감을 유발하는 장기 실행 스타일 다시 계산 이벤트가 있습니다.

이벤트 자체를 클릭하면 스타일 변경을 트리거한 JavaScript 지점을 정확히 찾아내는 호출 스택이 표시됩니다. 또한 변경으로 인해 스타일이 영향을 받는 요소 수(이 경우 400개가 넘는 요소)와 스타일 계산을 수행하는 데 걸린 시간을 확인할 수 있습니다. 이 정보를 사용하여 코드에서 수정 지점을 찾기 시작할 수 있습니다.

BEM 사양 사용

BEM의 코딩 접근 방식은 위에서 설명한 선택기 일치의 성능 이점을 실제로 통합합니다. 즉, 모든 요소에 대해 단일 클래스를 권장하고 계층 구조가 필요할 때 클래스 이름도 통합합니다(

.list { }
). -item { }

위처럼 수정자가 필요한 경우 마지막 하위 요소에 대해 특별한 작업을 수행하려면 다음과 같이 추가할 수 있습니다.

.list__list-item--last-child {}

CSS를 구성하는 좋은 방법을 찾고 있다면 BEM은 구조적 관점뿐만 아니라 스타일 조회가 단순화되기 때문에 시작하기 매우 좋은 곳입니다.

크고 복잡한 레이아웃과 레이아웃 불안을 방지하세요

레이아웃은 브라우저가 각 요소의 기하학적 정보(페이지에서의 크기와 위치)를 계산하는 프로세스입니다. 각 요소에는 사용된 CSS, 요소의 콘텐츠 또는 상위 요소에 따라 명시적이거나 암시적인 크기 정보가 있습니다. 이 프로세스를 Chrome, Opera, Safari 및 Internet Explorer에서는 레이아웃이라고 합니다. Firefox에서는 자동 리플로우(Reflow)라고 하는데, 실제로는 과정은 동일합니다.

스타일 계산과 유사하게 레이아웃 오버헤드에 대한 직접적인 고려 사항은 다음과 같습니다.

  1. 배치해야 하는 요소 수.

  2. 이러한 레이아웃의 복잡성.

  • 레이아웃의 범위는 일반적으로 전체 문서입니다.

  • DOM 요소의 수는 성능에 영향을 미치므로 레이아웃을 트리거하는 것은 최대한 피해야 합니다.

  • 레이아웃 모델의 성능을 평가하세요. 새로운 Flexbox는 이전 Flexbox 또는 부동 소수점 기반 레이아웃 모델보다 빠릅니다.

  • 강제 동기 레이아웃 및 레이아웃 스래싱을 ​​방지하세요. 스타일 값을 읽은 다음 스타일을 변경합니다.

레이아웃 작업을 최대한 피하세요

스타일을 변경할 때 브라우저는 변경 사항에 레이아웃 계산이 필요한지, 렌더 트리를 업데이트해야 하는지 여부를 확인합니다. "기하학적 속성 " (예: 너비, 높이, 왼쪽 또는 위쪽)으로 변경하려면 모두 레이아웃 계산이 필요합니다.

.box {
너비: 20px;
높이: 20px;
}
/**작고 보기 어리석은 동물 너비와 높이는 레이아웃을 트리거합니다. */
.box--expanded {
너비: 200px;
높이: 350px;
}

레이아웃 거의 항상 전체 문서에 영향을 미칩니다. 요소 수가 많으면 모든 요소의 위치와 크기를 파악하는 데 오랜 시간이 걸립니다.

레이아웃을 피할 수 없는 경우 Chrome DevTools를 사용하여 레이아웃에 걸리는 시간을 확인하고 레이아웃이 병목 현상의 원인인지 확인하는 것이 핵심입니다. 먼저 DevTools를 열고 "타임라인" 탭을 선택한 다음 "기록" 버튼을 클릭하고 사이트와 상호작용하세요. 녹화를 중지하면 사이트 성능에 대한 자세한 분석을 볼 수 있습니다.

위 예의 프레임을 자세히 보면 레이아웃에 20밀리초 이상이 소요되는 것을 알 수 있습니다. 애니메이션의 16밀리초 이 레이아웃은 프레임을 화면에 표시하는 데 너무 오래 걸립니다. 또한 DevTools가 트리의 크기(이 경우 1618개 요소)와 배치해야 하는 노드 수를 표시하는 것을 볼 수 있습니다.

이전 레이아웃 모델 대신 Flexbox를 사용하세요

웹 페이지에는 다양한 레이아웃 모델이 있으며 일부 모드는 다른 모드보다 더 광범위하게 지원됩니다. 초기 CSS 레이아웃 모델에서는 상대적으로, 절대적으로 또는 부동 요소를 통해 화면에 요소를 배치할 수 있는 기능을 제공했습니다.

아래 스크린샷은 1,300개의 상자에서 부동 소수점을 사용하는 레이아웃 오버헤드를 보여줍니다. 물론 이는 대부분의 응용 프로그램이 요소 위치를 지정하기 위해 다양한 수단을 사용하기 때문에 인위적인 예입니다.

Flexbox(웹 플랫폼의 새로운 모델)를 사용하도록 이 예제를 업데이트하면 다른 상황이 나타납니다.

이제 동일한 수의 요소와 동일한 시각적 모양에 대해 레이아웃 시간 훨씬 적습니다(이 경우 각각 3.5ms 및 14ms). 어떤 경우에는 Flexbox가 부동 소수점만큼 지원되지 않기 때문에 옵션이 아닐 수도 있다는 점을 기억하는 것이 중요합니다. 그러나 가능한 경우 최소한 레이아웃 모델이 웹 사이트 성능에 미치는 영향을 연구하고 실행 오버헤드를 최소화하는 모델을 채택해야 합니다. 웹 페이지.

어쨌든 Flexbox를 선택하든 안 하든 애플리케이션의 스트레스가 높은 지점에서는 레이아웃을 전혀 트리거하지 않도록 해야 합니다!

레이아웃 강제 동기화 방지

将一帧送到屏幕会采用如下顺序:

 

首先 JavaScript 运行,然后计算样式,然后布局。但是,JavaScript 在更改元素样式后,获取其几何属性的值,此时会强制浏览器应用新样式提前执行布局,值后才能获取几何属性值。这被称为强制同步布局(forced synchronous layout

要记住的第一件事是,在 JavaScript 运行时,来自上一帧的所有旧布局值是已知的,并且可供您查询。因此,如果(例如)您要在帧的开头写出一个元素(让我们称其为“框”)的高度,可能编写一些如下代码:

// Schedule our function to run at the start of the frame.requestAnimationFrame(logBoxHeight);function logBoxHeight() {  // Gets the height of the box in pixels and logs it out.  console.log(box.offsetHeight);
}

如果在请求此框的高度之前,已更改其样式,就会出现问题:

function logBoxHeight() {
  box.classList.add('super-big');    //样式更改后,浏览器必须先应用新的样式(重绘)之后才能获取当前的值,有时是多做无用功
  // Gets the height of the box in pixels and logs it out.  console.log(box.offsetHeight);
}

现在,为了获得框的高度,浏览器必须先应用样式更改(由于增加了 super-big 类),然后运行布局,这时才能返回正确的高度。这是不必要的,并且可能开销很大。

因此,始终应先批量读取样式并执行(浏览器可以使用上一帧的布局值),然后执行任何赋值操作。

以上函数应为:

function logBoxHeight() {  // Gets the height of the box in pixels and logs it out.  console.log(box.offsetHeight);
  box.classList.add('super-big');
}

大部分情况下,并不需要先应用新样式然后查询值,使用上一帧的值就足够了。与浏览器同步(或比其提前)运行样式计算和布局可能成为瓶颈。

避免布局超负荷(thrashing)

有一种方式会使强制同步布局更糟:连续执行大量这种强制布局。如下:

function resizeAllParagraphsToMatchBlockWidth() {  // Puts the browser into a read-write-read-write cycle.
  for (var i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = box.offsetWidth + 'px';
  }
}

此代码循环处理一组段落,并设置每个段落的宽度以匹配一个称为“box”的元素的宽度。这看起来没有害处,但问题是循环的每次迭代读取一个样式值 (box.offsetWidth),然后立即使用此值来更新段落的宽度 (paragraphs[i].style.width)。在循环的下次迭代时,浏览器必须考虑样式已更改这一事实,因为 offsetWidth 是上次请求的(在上一次迭代中),所以它必须应用更改的样式,然后运行布局。每次迭代都将出现此问题!

此示例的修正方法还是先读取值,然后写入值:

// Read.var width = box.offsetWidth;function resizeAllParagraphsToMatchBlockWidth() {  for (var i = 0; i < paragraphs.length; i++) {// Now write.paragraphs[i].style.width = width + 'px';
  }
}

如果要保证安全,应当查看 FastDOM,它会自动批处理读取和写入,应当能防止意外触发强制同步布局或布局抖动。

简化绘制的复杂度、减小绘制区域

绘制是填充像素的过程,像素最终合成到用户的屏幕上。 它往往是管道中运行时间最长的任务,应尽可能避免此任务。

  • 除 transform 或 opacity 属性之外,更改任何属性始终都会触发绘制。

  • 绘制通常是像素管道中开销最大的部分,应尽可能避免绘制。

  • 通过layer promotion和动画的编排来减少绘制区域。

  • 使用 Chrome DevTools paint profile来评估绘制的复杂性和开销;应尽可能降低复杂性并减少开销。

触发布局与绘制

如果触发布局,则总是会触发绘制,因为更改任何元素的几何属性意味着其像素需要修正!

 

如果更改非几何属性,例如背景、文本或阴影,也可能触发绘制。在这些情况下,不需要布局,并且管道将如下所示:

 

使用 Chrome DevTools 快速确定绘制瓶颈

Chrome DevTools를 사용하면 그리는 영역을 빠르게 결정할 수 있습니다. DevTools를 열고 키보드의 Esc 키를 누르세요. 표시되는 패널에서 "렌더링" 탭으로 이동하여 "페인트 직사각형 표시"를 선택하세요.

이 옵션을 켜면 Chrome은 그림을 그릴 때마다 화면이 녹색으로 깜박이게 합니다. 전체 화면이 녹색으로 깜박이는 것을 보거나 그려서는 안 되는 화면 영역이 보이면 추가 조사를 해야 합니다.

Chrome DevTools 타임라인에는 추가 정보를 제공하는 옵션인 Draw Profiler가 있습니다. 이 옵션을 활성화하려면 타임라인으로 이동하여 상단의 "페인트" 상자를 선택하세요. 참고로 이 옵션은 그리기 문제를 분석할 때만 켜야 합니다. 오버헤드가 발생하고 프로파일링 결과에 영향을 미칠 수 있기 때문입니다. 정확히 무엇이 그려지고 있는지 더 깊이 이해하고 싶을 때 가장 적합합니다.

위 설정을 완료한 후 이제 타임라인 녹화를 실행할 수 있으며, 드로잉 기록에는 더 자세한 내용이 포함됩니다. 프레임의 그리기 기록을 클릭하면 해당 프레임에 대한 그리기 분석기로 들어갑니다.

그리기 분석기를 클릭하면 어떤 요소가 그려졌는지, 시간이 얼마나 걸렸는지, 그리고 무엇이 그려졌는지 확인할 수 있는 보기가 나타납니다. 필수 개별 그리기 호출:

이 프로파일러는 면적과 복잡성(효과적으로 그리는 데 소요된 시간)을 표시하며, 그리기를 피하는 것이 옵션이 아닌 경우 둘 다 관리할 수 있습니다.

움직이거나 희미해진 요소 강화

그림이 항상 메모리의 단일 이미지에 그려지는 것은 아닙니다. 실제로 필요한 경우 브라우저는 여러 이미지 또는 합성기 레이어를 그릴 수 있습니다.

이 방법의 장점은 변형을 통해 화면에서 정기적으로 다시 그려지거나 이동되는 요소를 다른 요소에 영향을 주지 않고 처리할 수 있다는 것입니다. 개별 레이어를 처리하고 합성하여 최종 이미지를 만들 수 있는 Sketch, GIMP 또는 Photoshop과 같은 아트 파일에도 마찬가지입니다.

새 레이어를 만드는 가장 좋은 방법은 will-change CSS 속성을 사용하는 것입니다. 이 방법은 Chrome, Opera 및 Firefox에서 작동하며 변환 값을 전달하면 새 컴포지터 레이어가 생성됩니다. Safari 및 Mobile Safari와 같이 레이어 생성의 이점을 활용하는 브라우저는 3D 변환을 사용하여 새 레이어를 강제로 생성해야 합니다.

.moving-element {
변환: 번역Z(0);
}

하지만 다음과 같은 경우가 있습니다. 주의 사항: 각 레이어에는 메모리와 관리 오버헤드가 필요하므로 레이어를 너무 많이 만들지 마세요.

요소를 새 레이어로 승격한 경우 DevTools를 사용하여 그렇게 함으로써 성능이 향상되었는지 확인하세요.
요소를 분석하지 않고 홍보하지 마세요.

그리기 영역을 줄여보세요

하지만 때로는 요소가 개선되더라도 여전히 그리기 작업이 필요한 경우가 있습니다. 그리기 문제의 가장 큰 문제점은 브라우저가 그려야 하는 두 영역을 결합하고 이로 인해 전체 화면이 다시 그려질 수 있다는 것입니다. 따라서 페이지 상단에 고정된 헤더가 있고 화면 하단에 요소가 그려지는 경우 전체 화면이 다시 그려질 수 있습니다.

그리기 영역을 줄이는 것은 종종 애니메이션과 변형이 너무 많이 겹치지 않도록 배열하거나 페이지의 특정 부분에 애니메이션을 적용하지 않으려는 문제입니다.

그리기 복잡성 줄이기

그림의 경우 일부 그림은 다른 그림보다 비용이 더 많이 듭니다. 예를 들어, 흐림(예: 그림자)과 관련된 요소를 그리는 것은 빨간색 상자를 그리는 것보다 시간이 더 오래 걸립니다. 그러나 CSS에서는 이것이 항상 명확하지는 않습니다: background: red 및 box-shadow: 0, 4px, 4px, rgba(0,0,0,0.5); 반드시 완전히 다른 성능 특성을 갖는 것 같지는 않지만 그렇습니다.

위의 그리기 분석기를 사용하면 효과를 얻기 위해 다른 방법을 찾아야 하는지 판단할 수 있습니다. 보다 저렴한 스타일 세트나 대체 방법을 사용하여 최종 결과를 얻을 수 있는지 자문해 보세요.

특히 애니메이션 효과에서는 그리는 것을 최대한 피하고 싶습니다. 프레임당

10

ms

의 시간 예산은 일반적으로 특히 모바일 장치에서 드로잉 작업을 완료하는 데 충분하지 않기 때문입니다.

위 내용은 브라우저 렌더링 성능 최적화의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.