Reflow, repaint

2021년 02월 26일, 18:20

간단한 브라우저 렌더링 과정

  1. HTML과 CSS파일은 각각 HTML, CSS 파서를 통해 파싱한다.
  2. DOM트리, CSSOM(Css object model)이 만들어진다.
  3. DOM트리와 CSSOM을 합쳐서 렌더 트리를 생성한다.

Reflow

  • 렌더트리를 다시 만들고 그리는(repaint) 과정
  • 모든 엘리멘트의 위치와 길이 등을 다시 계산하는 과정(문서 일부 혹은 전체를 다시 렌더링)
  • reflow가 일어나는 경우
    • DOM 엘리먼트 추가, 제거 또는 변경
    • CSS 스타일 추가, 제거 또는 변경
      • CSS 스타일을 직접 변경하거나, 클래스를 추가함으로써 레이아웃이 변경될 수 있다. 엘리먼트의 길이를 변경하면, DOM 트리에 있는 다른 노드에 영향을 줄 수 있다.
    • CSS3 애니메이션과 트랜지션
      • 애니메이션의 모든 프레임에서 리플로우가 발생한다.
    • offsetWidth 와 offsetHeight 의 사용
      • offsetWidth 와 offsetHeight 속성을 읽으면, 초기 리플로우가 트리거되어 수치가 계산된다.
    • 유저 행동
      • 유저 인터랙션으로 발생하는 hover 효과, 필트에 텍스트 입력, 창 크기 조정, 글꼴 크기 변경, 스타일시트 또는 글꼴 전환등을 활성화하여 리플로우를 트리거할 수 있다.

Repaint

  • 렌더트리를 다시 그리는 과정
  • background-color, visibillty, outline 등과 같은 스타일 변경 시에는 레이아웃 수치가 변경 X -> repaint만 일어남

Reflow 최적화

  • js로 스타일을 변경하는 경우 cssText, class을 사용한다.
    // reflow가 2번 발생!! bad
    element.style.height = "700px";
    element.style.width = "600px";
    
    //reflow가 1번 발생!! good
    element.style.cssText = "width:600px; height:700px";
    
  • 클래스 변화에 따른 스타일을 변경할 경우 최대한 DOM트리 끝단의 노드를 선택한다.
      Reflow의 반경을 최소한.
      레이아웃에 영향을 받는 노드들이 적으면 적을수록 지출되는 비용이 적어짐.
    
  • table 태그의 사용을 피한다.
      테이블 태그는 점진적으로 불러와지는게 아닌 모두 불려지고 난 뒤에 렌더링이 됨.
      작은 변경만 일어나도 다른 모든 노드들에 대해서도 reflow가 일어난다.
    
  • 성능을 챙기려면 부드러운 애니메이션은 자제한다.
  • 애니메이션을 가진 노드는 position을 fixed, absolute로 사용한다.(다른 엘리먼트의 레이아웃에 영향을 최소한으로.)
  • 인라인 스타일의 사용을 배제한다.
      스타일 속성을 통해 스타일을 설정하면, 리플로우가 발생한다.
      엘리먼트의 클래스가 변경될 때 엘리먼트는 하나의 리플로우만 발생시킨다.
      인라인 스타일은 HTML 이 다운로드될 때, 레이아웃에 영향을 미치면서 추가 리플로우를 발생시킨다.
    
  • 숨겨진 엘리먼트를 변경한다.
      display: none; 으로 숨겨진 엘리먼트는 변경될 때, 리페인트나 리플로우를 일으키지 않는다. 
      엘리먼트를 표시하기 전에 엘리먼트를 변경한다.
    
  • js에서 돔을 추가하는 경우 DOM Fragment를 이용한다.
    const frag = document.createDocumentFragment();
    const ul = frag.appendChild(document.createElement('ul'));
    
    for (let i = 1; i <= 3; i++) {
      li = ul.appendChild(document.createElement('li'));
      li.textContent = `item ${ i }`;
    }
    
    document.body.appendChild(frag);
    
  • 캐싱을 활용하자
      offset, scrollTop과 같은 경우에는 정확한 값을 계산하기 위해서 reflow를 발생시킨다.
      offset, scrollTop을 여러번 호출하지 않게 변수로 저장해서 사용한다.
    

참고