Repaint는 렌더 트리가 탐색되고 paint 메서드가 호출되어서 UI 기반의 구성요소를 사용해서 그리는 과정이다. repaint가 이루어지기 위해서는 렌더트리가 있어야 하고 따라서 Reflow 작업이 이루어진 후에 repaint 작업이 이루어지는 것을 알 수 있다. 화면의 구조가 변경이 될 때는 reflow와 repaint가 모두 발생한다.
다만 repaint가 발생하기 위해서 항상 reflow가 발생해야 하는 것은 아니다. reflow가 발생하지 않고 repaint만 발생하는 경우도 있는데, 예를 들면 레이아웃에 영향을 주지 않는 엘리먼트 개별의 변화에 대해서는 repaint만 발생한다.
function rePaint() {
document.getElementById('container').style.backgroundColor = 'red';
return false;
}
rePaint();
Reflow는 렌더링 엔진에서 요소를 배치하는 과정을 의미한다. 위에서 본 렌더 트리 구축 단계에서 DOM 트리와 스타일 규칙을 합쳐서 렌더 트리를 만들고, 여기에서 reflow를 통해 각각의 요소들의 레이아웃을 위치시킨다.
여기서 렌더 트리는 DOM 요소를 기반으로 만들어지지만, 완전히 대응되지는 않는다. DOM 트리가 문서의 구조를 나타낸다면 렌더 트리는 문서의 시각적 구조를 나타낸다. 예를 들어 스타일에 display: none 속성이 있다면 DOM에는 존재하지만 시각적으로는 없기에 렌더 트리에는 할당되지 않는다.
reflow가 발생하는 경우는 다음과 같다. (여기에 적은 케이스는 예시일 뿐, 이 외에도 reflow가 발생할 수 있는 경우는 물론 더 있다)
function reFlow() {
document.getElementById('container').style.width = '600px';
return false;
}
reFlow();
Repaint의 경우, Visibility를 DOM API을 통해 조절했을 때 자식 노드들까지 다 검색하기 때문에 성능 저하를 발생시킬 수도 있다.
스타일을 변경할 경우 가장 하위 노드의 클래스를 변경한다.
DOM 노드의 크기 또는 위치가 변경되면 하위 노드와 상위 노드에도 영향을 미칠 수 있다. 이 때 가장 하위 노드의 스타일을 변경할 경우, 전체 노드가 아닌 일부 노드로 reflow를 영향을 최소화 할 수 있다.
인라인 스타일을 사용하지 않는다.
인라인 스타일은 HTML이 파싱될 때, 레이아웃에 영향을 미쳐 추가 리플로우를 발생시킨다.
애니메이션이 있는 노드는 position을 fixed 또는 absolute로 지정한다.
애니메이션 효과는 많은 reflow 비용을 발생시킨다. position 속성을 fixed 또는 absolute의 값으로 주어, 지정된 노드를 전체 노드에서 분리시켜 해당 노드에서만 reflow가 발생하도록 제한시킬 수 있다.
애니메이션 효과를 줘야 하는 노드에 position 속성이 적용이 되지 않았다면 애니메이션 시작 시 position 속성 값을 fixed 또는 absolute로 변경하였다가 애니메이션 종료 후 다시 원복시켜서 렌더링을 최적화 할 수 있다.
table 레이아웃을 피한다.
table은 점진적으로 렌더링 되지 않고, 모두 로드되고 테이블 너비가 계산된 후에 화면에 그려진다. 테이블 안의 컨텐츠의 값에 따라 테이블 너비가 계산 된다. 따라서 테이블 컨텐츠의 작은 변경만 있어도 테이블 너비가 다시 계산되고 테이블의 모든 노드들이 reflow가 발생한다. 이러한 이유로 table을 레이아웃 용도로 사용하는 일은 피해야 한다.
CSS 하위 선택자를 최소화 한다.
CSS 하위 선택자를 최소화 하는 것이 렌더링 성능에 더 좋다.
렌더 트리는 DOM과 CSSOM이 합쳐져서 만들어진다. DOM은 HTML이 파싱되어 만들어진 트리이고, CSSOM은 CSS가 파싱되어 만들어진 트리이다. 두 트리를 결합하여 렌더 트리를 만드는데, CSS 하위 선택자가 많아지면 CSSOM 트리의 깊이(Depth)가 깊어지게 되고 결국 렌더 트리를 만드는 시간이 더 오래 걸리게 된다.
숨겨진 노드의 스타일을 변경한다.
display: none 으로 숨겨진 노드를 변경할 때는 reflow가 발생하지 않는다. 숨겨진 노드를 표시하기 전에 노드의 컨텐츠를 먼저 변경한 후 화면에 나타내면 reflow를 줄일 수 있다.
DOM 사용을 최소화한다.
reflow 비용을 줄이기 위해서 DOM 노드 사용을 최소화 해야 한다.
예를 들어 DOM Fragment를 사용하여 DOM을 추가할 때 마다 DOM 접근을 최소화 하는 방법이있다.
캐시를 활용한다.
브라우저는 레이아웃 변경을 큐에 저장했다가 한 번에 실행하여 reflow를 최소화 한다. 하지만 offset, scrollTop 과 같은 계산된 스타일 정보를 요청할 때마다 정확한 정보를 제공하기 위해 큐를 비우고 모든 변경사항을 적용한다.