리플로우와 리페인팅에 대해 알아봅니다.
생성된 DOM 노드의 레이아웃 수치(너비, 높이, 위치 등) 변경 시 영향 받은 모든 노드의(자신, 자식, 부모, 조상(결국 모든 노드) ) 수치를 다시 계산하여(Recalculate), 렌더 트리를 재생성하는 과정이며 또한, Reflow
과정이 끝난 후 재 생성된 렌더 트리를 다시 그리게 되는데 이 과정을 Repaint
라 합니다.
function reFlow() {
document.getElementById('container').style.width = '600px';
return false;
}
- Click 이벤트 handler
- Recalcurate(변경된 스타일 수치 계산 수행)
- Layout(Reflow 과정 수행)
- Paint(Repaint 과정 수행)
Reflow 발생 이유와 같이 스타일의 모든 변경이 레이아웃 수치에 영향을 받는것은 아닙니다.
즉, 아래 그래프 처럼 노드의 background-color, visibillty, outline 등의 스타일 변경 시에는 레이아웃 수치가 변경되지 않으므로 Reflow 과정이 생략된 Repaint 과정만 일어나게 됩니다.
function rePaint() {
document.getElementById('container').style.backgroundColor = 'red';
return false;
}
- Click 이벤트 handler
- Recalcurate(변경된 스타일 수치 계산 수행)
- Paint(Repaint 과정 수행)
- 노드의 추가 또는 제거시.
- 요소의 위치 변경 시.
- 요소의 크기 변경 시.(margin, padding, border, width, height, 등..)
- 폰트 변경 과(텍스트 내용) 이미지 크기 변경 시.(크기가 다른 이미지로 변경 시)
- 페이지 초기 랜더링 시.(최초 Layout 과정)
- 윈도우 리사이징 시.
-
가급적 말단에 위치한 노드 수치 변경 시 리플로우 수행 반경을 전체 노드가 아닌 일부 노드로 제한 시킬 수 있습니다.
-
즉, Reflow 수행 비용을 줄일 수 있다는 말과 같습니다.(하지만 실무 작업 시 적용 가능한 범위가 크지 않다)
- 적용 시 코드 가독성과 Reflow 비용을 줄일 수 있습니다.
-
보통 (JS(Javascript) + CSS)를 활용한 에니메이션 효과는 해당 프레임에 따라 무수히 많은 Reflow 비용이 발생하게 됩니다.
-
하지만 position 속성을 "fixed" 또는 "absoute"로 값을 주면 지정된 노드는 전체 노드에서 분리됩니다.
-
즉, 전체 노드에 걸쳐 Reflow 비용이 들지 않으며, 해당 노드의 Repaint 비용만 들어가게 됩니다.
-
또한, 노드의 position 값을 초기에 적용하지 않았더라도 에니메이션 시작 시 값을 변경(fixed, absolute)하고 종료 시 다시 원복 시키는 방법을 사용해도 무관합니다.
<script>
function animation() {
document.getElementById('container_animation').style.left = '100px';
document.getElementById('container_animation').style.top = '100px';
return false;
}
</script>
<div id="container_animation" style="background:blue;position:absolute;top:0px;left:0px;width:100px;height:100px;border:red 1px solid;"></div>
-
애니메이션 효과는 보통 무수히 많은 Reflow 비용이 들어가게 됩니다.
-
물론, 디바이스 환경에 따라 큰 성능 차이를 보일 수 있겠지만, 스마트폰 기기와 같은 경우 그리 좋은 성능을 보이지 못합니다.
-
따라서 에니메이션 효과와 같은 CPU 퍼포먼스 비용이 큰 작업은 언제나 퀼리티와 퍼포먼스 사이에 적당한 타협이 필요합니다.
-
테이블로 구성된 페이지 레이아웃은 점진적(progressive) 페이지 렌더링이 적용되지 않으며, 모두 로드되고 계산(Recalculate)된 후에야 화면에 뿌려지게 됩니다.
-
하지만 해당 테이블에 table-layout:fixed 속성을 주는 것이 디폴트값인 auto에 비해 성능면에서 더 좋다고 한다.
-
이미 오래된 규칙이지만 매우 효과적인 규칙입니다.
-
CSS 표현식(expression)의 비용이 매우 높은 이유는, 문서 전체 또는 문서 중 일부가 Reflow될 때마다 표현식이 다시 계산되기 때문입니다.
-
결국 애니메이션과 같은 변화에 의해 리플로우가 발생했을 때, 경우에 따라 초당 수천, 수만번의 표현식 계산이 진행될 수 있다는 것을 의미합니다.
.expression {
width: expression(document.documentElement.clientWidth > 0 ? '1000px' : 'auto');
}
- DOM과 스타일 변경을 하나로 묶어 리플로우 수행을 최소화 한다.
function collect() {
var elem = document.getElementById('container');
elem.style.backgroundColor = 'red';
elem.style.width = '200px';
elem.style.height = '200px';
return false;
}
function collect() {
var elem = document.getElementById('container');
elem.style.cssText = 'background:red;width:200px;height:200px;';
return false;
}
function collect() {
var elem = document.getElementById('container');
elem.className = 'collect';
return false;
}
첫 번째 상황: 112ms
두 번째 상황: 104ms
세 번째 상황: 104ms
-
브라우저는 레이아웃 변경을 큐에 저장했다가 한번에 실행하는 방법으로 Reflow 수를 줄입니다.
-
하지만 offset, scrollTop, scrollLeft, 값과 같은 계산된 스타일 정보를 요청할 때마다 정확한 정보를 제공하기 위해 큐를 비우고 모든 변경을 다시 적용합니다.
-
즉, 중복되는 수치에 대한 스타일 정보를 변수에 저장(캐쉬)해 요청수를 줄임으로써 Reflow 비용을 최소화시킵니다.
function collect() {
var elem = document.getElementById('container');
var cw = elem.style.width;
return parseInt(cw, 10) * parseInt(cw + document.documentElement.clientWidth, 10);
return false;
}
- 비록, 실무에서 사용되는 코드는 아니지만, 해당 규칙만 이해하면 좋을 것입니다.
- 아래 포스트와 같이 여러 가지 방법을 활용하여 Reflow 비용을 줄일 수 있습니다.