reflow, repaint
생성된 DOM노드의 레이아웃 수치(너비, 높이, 위치 등) 변경 시 영향 받은 모든 노드의 수치를 다시 계산하여, 렌더 트리를 재생성하는 과정이다.
또한, Reflow 과정이 끝난 후, 재 생성된 렌더 트리를 다시 그리게 되는데, 이 과정을 Repatin라 한다.
문서 내 노드들의 레이아웃 포지션을 재계산 후 다시 뿌려주므로 Repaint보다도 더 심각한 퍼포먼스 저하를 유발시키는 프로세스이다.
특정 엘리먼트에 대해 Reflow 발생 시, 페이지에서의 해당 요소는 즉시 Reflow State가 되며, 해당 엘리먼트의 자식요소와 부모/조상 요소 역시 레이아웃 계산을 진행한다.
결국 페이지 전체를 다시 훑는 거나 마찬가지
모든 엘리먼트의 위치와 길이 등을 다시 계산하는 것으로 문서의 일부 혹은 전체를 다시 렌더링한다.
어떠한 액션이나, 이벤트에 따라 HTML 요소의 크기나 위치 등 레이아웃 수치를 수정하면 그에 영향을 받는 자식 노드나 부모 노드들을 포함하여,
Layout 과정을 다시 수행하게 됩니다. 이렇게 되면 Render Tree와 각 요소들의 크기와 위치를 다시 계산하게 됩니다.
Reflow 과정이 끝난 후 재 생성된 렌더 트리를 다시 그리게 되는데 이 과정을 Reapint라 한다.
엘리먼트의 스킨에 변화가 발생하지만, 레이아웃에 영향을 미치지 않을 때 유발됩니다. (Visibility, outline, background-color 등 포함)
Opera에 따르면 해당 행위가 발생하는 순간, 문서 내 DOM tree의 다른 노드들의 스킨까지도 검증해야 하므로 비용이 높다고 함
레이아웃에 영향을 주지 않지만, 가시성에는 영향을 주는 엘리먼트가 변경되면 발생한다. 예를들면, opacity, background-color, visibility, outline
Opera에 따르면, "브라우저가 DOM 트리에 있는 다른 모든 노드의 가시성을 확인해야 하므로 리페인트 비용이 많이 든다"고함
Reflow만 수행되면 실제 화면에 반영되지 않습니다. Render Tree를 다시 화면에 그려주는 과정이 필요하다. 결국 Paint 단계가 다시 수행되는 것
DOM 노드의 변경: 추가, 제거 업데이트
DOM 노드의 노출 속성을 통한 변경: display:none은 reflow와 repaint를 발생시키지만, visibility:hidden은 요소가 차지한 영역을 유지해 리페인트만 발생시킨다.
스크립트 애니메이션: 애니메이션은 DOM 노드의 이동과 스타일 변경이 짧은 시간 내에 수차례 반복해 발생되는 작업이다.
스타일: 새로운 스타일시트의 추가 등을 통한 스타일 정보 변경 or 기존 스타일 규칙의 변경
사용자의 액션: 브라우저의 크기 변경, 글꼴 크기 변경
1) Use Best-Practice Layout Techniques
레이아웃을 위해 인라인 스타일이나 테이블을 사용하지 않아야 한다.
인라인 스타일은 HTML이 다운로드되고, 추가 reflow 트리거 할 때, 레이아웃에 영향을 미칩니다. 테이블은 셀 치수를 계산하기 위해 파서가
둘 이상의 패스를 필요로 하기 때문에 비용이 많이 듭니다.
테이블 레이아웃 사용: fixed는 열 너비는 헤더 행 내용을 기반으로 하므로 표 형식의 데이트를 표시할 때 도움이 됩니다.
flexbox의 위치와 크기가 HTML이 다운로드 될 때, 변경될 수 있기 때문에 메인 페이지 레이아웃에 flex 아이템을 사용하면 성능 저하가 될 수 있다.
2) CSS 규칙의 수를 최소화한다.
사용하는 규칙이 적을수록 reflow가 빨라진다. 가능한 경우 복잡한 CSS selector를 피해야한다.
이 문제는 부트스트랩과 같은 프레임워크를 사용하는 경우 특히 문제가 될 수 있다. 사용되지 않는, CSS, uCss, grunt-ucss, gulp-ucss와 같은 도구는
스타일의 도구와 파일의 크기를 상당히 줄일 수 있다.
3) DOM의 깊이를 최소화 한다.
4) 하단의 DOM 트리에서 클래스를 변경한다.
5) 복잡한 애니메이션 flow에서 제거합니다.
6) 숨겨진 요소를 변경합니다.
7) Batch로 요소 업데이트
-한 번의 자업으로 모든 DOM 요소를 업데이트하여 향상시킬 수 있다.
var myelement = document.getElementById('myelement');
myelement.width = '100px';
myelement.height = '200px';
myelement.style.margin = '10px';
이 케이스는 한번에 3번의 reflow를 발생시킬 수 있다.
var myelement = document.getElementById('myelement');
myelement.classList.add('newstyles');
.newstyles {
width: 100px;
height: 200px;
margin: 10px;
}
또한, DOM을 건드리는 시간을 최소화 할 수 있다. bullet 목록을 만들겠다고 가정하면
각 요소를 하나씩 추가하면 한번에 최대 7개의 reflow가 발생한다. (하나의
var
i, li,
frag = document.createDocumentFragment(),
ul = frag.appendChild(document.createElement('ul'));
for (i = 1; i <= 3; i++) {
li = ul.appendChild(document.createElement('li'));
li.textContent = 'item ' + i;
}
document.body.appendChild(frag);
8) 영향을 받는 요소를 제한한다.
9) 부드러운 애니메이션은 성능을 떨어트린다는 것을 알아야한다.
10) 브라우저 툴로 repaint 이슈 분석