브라우저 렌더링 과정을 알아보았으니 최적화시키는 방법을 알아보자.
위와 같은 렌더링 과정에서 Reflow와 Repaint는 둘다 비용이 많이 드는 작업이다.
먼저 이 두 과정에 대해 알아보자.
Reflow(Layout)이란?
화면구조(Layout)이 변경되었을 때 뷰 포트 내에서 렌더 트리상 노드의 정확한 위치과 크기를 계산하는 과정이다. element의 reflow는 DOM에 있는 모든 하위, 상위 요소의 후속 리플로우를 유발한다.
- 모든 엘리먼트의 위치나 길이, 크기 등등을 다시 계산하는 과정
- 상위 엘리먼트를 변경시키면 하위 엘리먼트에도 영향을 끼침
- render tree를 재생성하므로 부하가 크고 레이아웃에 영향을 줌
- DOM노드를 추가, 제거 , 업데이트하는 경우 발생
Repaint(Redraw)란?
화면에 가시성이 변하지만 레이아웃에 영향을 미치지 않는 요소의 외관을 변경할 때 발생한다.
- 레이아웃에 영향을 주지않지만 눈에 보이는 요소들(background-color, color, visibility,..)이 변경됨
- reflow 보다는 부하가 크지는 않음
Reflow와 Repaint가 모두 일어나는 경우
- DOM 노드를 추가, 제거 업데이트하는 경우
- DOM 요소의 위치 변경, 크기 변경 (margin, padding, border, width, height, 등..)
- display : none으로 DOM 요소를 숨기는 경우
- DOM 노드를 이동하거나 애니메이션을 생성하는 경우
- 창 크기를 조정하는 경우 (Resizing)- 글꼴 스타일을 변경하는 경우 (요소의 geometry가 변경되고 이는 페이지에 있는 다른 요소의 위치나 크기에 영향을 미칠 수 있고 두 요소 모두 브라우저에서 reflow를 수행하고 repaint 과정을 거침)- 스타일 시트를 추가하거나 제거하는 경우- DOM을 조작하는 스크립트를 수정하는 경우
- offset, scrollTop, scrollLeft와 같은 계산된 스타일 정보 요청
- 이미지 크기 변경
Repaint만 일어나는 경우
- visibility : hidden 으로 DOM 요소를 숨기는 경우 (레이아웃이나 위치 변경이 없어 repaint만 발생)
- background-color, visibillty, outline 등의 스타일 변경
Reapint와 Reflow 최소화하는 방법
1) 개별 스타일을 바꾸기보다 클래스 이름을 변경. 동적인 스타일인 경우 cssText속성을 편집한다.
2) DOM 변경사항을 일괄처리
- documentFragment를 사용하여 DOM사용을 최소화한다. ( documentFragment는 DOM에 적용되기 전까지는 메모리상에만 존재)
- display :none으로 요소를 숨기고 (1 reflow, 1 repaint) 변경사항 100개를 추가한 후 display를 복원한다. ( 총 2 reflow, 2 repaint)
3) 계산된 스타일을 반복적으로 묻지 않고 변수에 캐싱
:offset, scrollTop과 같은 계산된 스타일 정보를 요청할 때마다 정확한 정보를 제공하기 위해 큐를 비우고 모든 변경사항을 적용하기 때문에 스타일 정보를 변수에 저장하여 사용을 권장
4) 영향받는 엘리먼트 제한하기 ( position fixed, absolute를 활용)
5) <table> 레이아웃을 피한다.
:<table>은 점진적으로 렌더링 되지 않고 모두 로드되고 테이블 너비가 계산된 후 화면에 그려진다. 콘텐츠의 값에 따라 테이블 너비가 계산되기 때문에, 테이블 콘텐츠의 작은 변경만 있어도 테이블 너비가 다시 계산되고 테이블의 모든 노드들이 Reflow가 발생. 데이터 표시용 도로 사용 시 table-layout:fixed를 사용하자.
6) IE의 CSS표현식을 사용하지 않는다.
: Reflow가 발생할 때마다 자바스크립트 표현식이 다시 계산되기 때문이다.
6) 되도록 실행 사이클 안에서 실행하도록 처리
타이머에서 실행되게 하면 추가적인 실행 사이클이 발생하게 되는데, 첫 번째 요소에 대한 작업이 한 사이클 내에서 실행되고, 타이머의 실행은 먼저 실행된 사이클이 끝난 다음에 진행된다. 이로 인해 결과적으로 리플로와 리페인트가 두 번 일어나게 되므로 리플로우와 리페인트가 일어날 수 있는 작업은 가능하면 실행 사이클 안에서 실행하도록 처리하는 편이 효과적이다.
7) 노드복제
: 변경하려는 요소의 노드를 복제한 후 복제된 노드에 필요한 작업을 실행하는 방법. 복제된 노드는 DOM 트리에 추가된 상태가 아니므로 렌더링 성능에 영향을 줄 수 있는 작업을 실행하더라도 리플로나 리페인트가 발생하지 않는다.
작업이 모두 완료된 이후 복제된 노드를 원래 노드와 치환해 DOM 트리에 변경된 사항이 적용되게하면 치환 시점에만 리플로와 리페인트가 발생한다.
var element = document.getElementById("box1");
var clone = element.cloneNode(true); // 원본 노드를 복제한다.
for(var i=0; i < 100; i++) {
clone.style.width = i + "px";
}
// 변경된 복제 노드를 DOM 트리에 반영하기 위해 기존 노드와 치환한다.
parentNode.replaceChild(clone, element);
Chorme Tool을 통한 성능 확인
-Bad Code
- Optimized Code
----
참고사이트
- <Reflow or Repaint(or ReDraw) 과정 설명 및 최적화 방법>
- <[Browser] Critical Rendering Path 최적화>
- <FrontEnd-성능 최적화-기본>
- <Understanding Reflow and Repaint in the browser>
'WEB' 카테고리의 다른 글
브라우저가 웹페이지를 그리는 방법 (렌더링) (0) | 2022.04.20 |
---|---|
GraphQL이란 (0) | 2022.02.23 |
웹 폰트와 최적화 (0) | 2021.11.30 |
SVG파일 미리보기 (0) | 2021.10.25 |
프로그레시브 웹앱 (PWA) (0) | 2021.08.05 |