requestAnimationFrame API는 왜 중요한가요?
불필요한 레이아웃을 피하는 방법과, 불가피하다면 성능 영향을 줄이는 방법을 알아봅니다.
아래 글과 이어서 보면 좋습니다.
https://itchallenger.tistory.com/838
Layout Thrashing
Layout Thrashing은 프레임 몇 개가 누락되어, 화면 렌더링이 버벅이는 것처럼 느껴지는 현상을 의미합니다.
- 보통 브라우저는 60fps로 화면을 업데이트 하는데, 각 화면 간의 업데이트 주기(약 16.6ms)가 달라진다면, 사용자는 렉을 느끼게 됩니다.
- 레이아웃 스래싱은 페이지가 '로드'되기 전에 웹 브라우저가 웹 페이지를 여러 번 리플로우하거나 다시 그려야 하는 경우 발생합니다.
// Read
var h1 = element1.clientHeight;
// Write (invalidates layout)
element1.style.height = (h1 * 2) + 'px';
// Read (triggers layout)
var h2 = element2.clientHeight;
// Write (invalidates layout)
element2.style.height = (h2 * 2) + 'px';
// Read (triggers layout)
var h3 = element3.clientHeight;
// Write (invalidates layout)
element3.style.height = (h3 * 2) + 'px';
그러나 현재 연산(또는 프레임)이 완료되기 전에 DOM에서 기하학적 값을 다시 요청하면
(write 후 read 요청 - 동일 프레임 생명주기)
브라우저가 레이아웃을 일찍 수행하도록 강제합니다.
이는 'forced synchonous layout(강제 동기 레이아웃)'으로 알려져 있으며 성능을 저하시킵니다!
최신 데스크톱 브라우저에서 레이아웃 스래싱의 부작용이 항상 분명한 것은 아닙니다.
그러나 저전력 모바일 장치에는 심각한 결과가 있습니다.
이상적인 해결 방법
이상적으로 우리는 DOM 읽기와 DOM 쓰기를 함께 일괄 처리할 수 있도록 실행 순서를 간단히 재정렬 합니다.
이 경우 우리는 문서를 한 번만 리플로우하면 됩니다.
// Read
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;
// Write (invalidates layout)
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h2 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';
// Document reflows at end of frame
이상과 현실의 괴리
requestAnimationFrame 사용하기
- 모든 DOM 쓰기를 다음 프레임에서 함께 실행되도록 예약하는 데 사용할 수 있기 때문에 매우 유용합니다.
- 모든 DOM 읽기는 현재 사이클의 태스크 큐에서 실행되도록 남겨둡니다.
// Read
var h1 = element1.clientHeight;
// Write
requestAnimationFrame(function() {
element1.style.height = (h1 * 2) + 'px';
});
// Read
var h2 = element2.clientHeight;
// Write
requestAnimationFrame(function() {
element2.style.height = (h2 * 2) + 'px';
});
실제 예제
주의 : reset 함수 때문에 벤치마킹이 이상할 수 있으니 반드시 새로고침 후 첫번째 결과만 확인하세요
아래 참고 글의 예제(working example)를 사용해 벤치마킹을 진행해 보겠습니다.
저는 새로고침 후 with layout thrash 버튼을 클릭하니
- 레이아웃이 각 돔 조작 이터레이션 마다 수행되었으며
- 레이아웃 시프트가 레이아웃 도중 지속적으로 발생했습니다.
requestAnimationFrame 함수를 사용하면 어떻게 될까요?
- var width = div.clientWidth 라인 때문에 스타일 재계산이 일어나지만, 레이아웃이 매 이터레이션 마다 발생하지 않습니다.
- 브라우저는 똑똑하기 때문에 해당 값이 조회된 이후로 변화된 점이 없으면 스타일 재계산을 다시 수행하지 않고 값을 리턴합니다.
- 스타일 재계산은 렌더링 주기에서 함께 일어나도록 스케쥴 됩니다.
그리고 브라우저는 우리가 원하는 대로 레이아웃을 배치 처리합니다.
참고
https://blog.wilsonpage.co.uk/preventing-layout-thrashing/
https://stackoverflow.com/questions/34698433/what-is-involved-in-chromes-recalculate-style-event
'FrontEnd' 카테고리의 다른 글
[객체지향설계] 객체지향 설계 및 분석 : UML과 Use Case 다이어그램 (0) | 2022.12.30 |
---|---|
[객체지향 설계] 객체지향 설계의 기초와 핵심개념 (0) | 2022.12.30 |
브라우저의 이벤트 루프와 렌더링의 관계에 대해 알아보자 (2) | 2022.12.28 |
[번역] JS 번들 분할의 모든 것 (0) | 2022.12.26 |
Vue3 컴포넌트 디자인 패턴 : expose (0) | 2022.12.25 |