JavaScript

브라우저 동작원리(= 렌더링 과정) + 리액트의 원리

milliwonkim 2022. 9. 21. 19:34
반응형
SMALL

https://mine-it-record.tistory.com/577

DOM(Document Object Model) 생성

- 생명주기: DOMContentLoaded, load, beforeunload, unload

1. DOMContentLoaded

=> DOM 트리 완성 즉시 발생함. 

=> img 및 CSS 등 외부 자원 기다리지 않음

<script>
  function ready() {
    alert('DOM이 준비되었습니다!');

    // 이미지가 로드되지 않은 상태이기 때문에 사이즈는 0x0입니다.
    alert(`이미지 사이즈: ${img.offsetWidth}x${img.offsetHeight}`);
  }

  document.addEventListener("DOMContentLoaded", ready);
</script>

<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">

=> 여러 스크립트 태그가 존재한다면 => 모든 스크립트가 실행된 이후 DOMContentLoaded가 실행됨

(이유: DOMContentLoaded에 DOM 조작 관련 로직이 있을 수 있기 때문)

<script>
  document.addEventListener("DOMContentLoaded", () => {
    alert("DOM이 준비되었습니다!");
  });
</script> // a

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script> // b

<script>
  alert("라이브러리 로딩이 끝나고 인라인 스크립트가 실행되었습니다.");
</script> // c

// 실행순서 b => c => a

=> document.createElement('script')로 동적으로 생성되고 웹페이지에 추가된 스크립트는 DOMContentLoaded를 막지 않습니다.

=> CSS 불러오는 link 태그 다음에 스크립트 위치 => CSS 로드 완료 전까지 스크립트 태그 실행안됨 => 따라서 DOMContentLoaded도 스타일 로드 완료 되야 발생함

(이유: JS 중 스타일의 영향을 받는 프로퍼티 사용 가능성 ex. img.offsetTop ...)

 

2. load

=> img 및 CSS 등 외부자원이 모두 불러와졌을 때 실행됨

 

3. beforeunload(페이지 떠나기 전 확인) / 

=> 사용자가 페이지를 떠날 때 발생

 

4. unload(진짜 떠났을 때 마지막으로 실행할 것)

=> 사용자가 페이지를 떠날 때 발생

 

// only document
window.addEventListener("DOMContentLoaded", (event) => {
    console.log("DOMContentLoaded");
});

// after resources (css, images)
window.addEventListener("load", (event) => {
    console.log("load");
});

// before unload
window.addEventListener("beforeunload", (event) => {
    console.log("beforeunload");
});

// resource is being unloaded
window.addEventListener("unload", (event) => {
    console.log("unload");
});

 

 

CSSOM 생성

- DOM + CSS = CSSOM

=> DOM + CSSOM = Render Tree

- display:none가 적용된 DOM => 최종 렌더트리에 포함 안함

- visibility: hidden / opacity: 0 => 최종 렌더트리에 포함(눈에 보이지는 않지만 요소는 존재)

HTML, CSS, JS 파싱(= 동기적 파싱)

- HTML을 파싱하는 중에 link 태그를 만날 경우 => 동시에 HTML, CSS 파싱을 함(= 블로킹 안함)

=> CSS는 DOM 구조를 변경하지 않음

- JS는 DOM 구조를 변경할 수 있어서 script 태그를 만난다면 HTML 파싱을 멈추고 JS 파싱을 시작한다

=> defer, async를 쓰면 블로킹 안함(비동기적 파싱)

 

* async

- JS다운로드는 백그라운드에서 실행됨

- JS로드는 비동기, JS 실행은 HTML 파싱 후(완료가 아니어도) => JS 실행 시 HTML 파싱 중단

- async가 여러개 있을경우, 실행 순서는 제각각

<script async src="https://javascript.info/article/script-async-defer/long.js"></script>
<script async src="https://javascript.info/article/script-async-defer/small.js"></script>

// small.js가 long.js보다 먼저실행될 수 있다

- DOMContentLoaded 이벤트와 async 스크립트는 서로를 기다리지 않습니다.

 

1. 페이지 구성이 끝난 후에 async 스크립트 다운로딩이 끝난 경우, DOMContentLoaded는 async 스크립트 실행 전에 발생할 수 있습니다,

2. async 스크립트가 짧아서 페이지 구성이 끝나기 전에 다운로드 되거나 스크립트가 캐싱처리 된 경우, DOMContentLoaded는 async 스크립트 실행 후에 발생할 수도 있습니다.

 

* defer

- JS다운로드는 백그라운드에서 실행됨

- JS로드는 비동기, JS 실행은 동기(HTML 파싱 완료 후 실행)

- DOMContentLoaded 이벤트 발생 전에 실행됨

<p>...스크립트 앞 콘텐츠...</p> // a

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> // b

<!-- 바로 볼 수 있네요! -->
<p>...스크립트 뒤 콘텐츠...</p> //c


// 실행순서 a => c => b

- 윗 스크립트 실행이 끝나고 아래 스크립트가 실행됨(작은스크립트인 small.js가 긴 스크립트인 long.js 보다 먼저 다운은됨)

<script defer src="https://javascript.info/article/script-async-defer/long.js"></script> // a
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script> // b

// a가 끝나야 b가 실행됨

- script에 src가 없으면 defer 속성은 무시됨(인라인 script 태그 무시)

// defer 적용 안됨
<script defer>
  document.addEventListener('DOMContentLoaded', () => alert("`defer` 스크립트가 실행된 후, DOM이 준비되었습니다!")); // (2)
</script>

// defer 적용됨
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

 

Render Tree 생성

Layout(Reflow)

- 렌더 트리의 각 노드들 => 어느 뷰포트에 어느 위치에 배치돼야하는지 계산

%, vh, vw 등 상대적인 속성이 px로 변환됨

Painting(Rendering)

- 렌더 트리의 각 노드들을 모니터에 실제 픽셀로 그림

- 리플로우가 발생되면 페인트는 반드시 발생되야한다 => 하지만 background-color, visibility 등과 같이 레이아웃에 영향을 안주는 속성이 있다면 리플로우할 필요 없음 => 따라서 페인트만 수행

 

Layout과 Paint 가 모두 발생하지 않는 속성

- transform, opacitiy, cursor, orphans, perspective 등

- 'Paint Only' 속성보다도 렌더링 속도가 더 빠름(연산이 절대적으로 줄어듬)

=> 가능하면 Repaint, Reflow가 일어나지 않는 속성을 사용하는 것을 권장한다.

- 브라우저의 렌더링 엔진 별로 CSS 속성 처리 단계가 다를 수 있음

 


브라우저 성능저하의 원인 => 리플로우, 리페인트

- 따라서 DOM 수정을 최소화해야한다 => SPA가 생긴 이유 => Virtual DOM을 통해 실제 DOM 수정을 최소화한다.

- DOM자체는 읽고 쓰는 성능은 자바스크립트 객체 처리 성능과 비슷 => 리플로우, 리페인팅에서 시간허비


가상돔(Virtual DOM) 특징

- 현재 돔의 복사본인 가상돔 A와, 변경이 있는 가상돔 B(메모리에 새로 생성)를 비교(휴리스틱 알고리즘으로 비교) => 차이점의 모든 부분을 일괄적으로, 리액트가 실제 DOM에 반영한다

- 배치(batch) => DOM 업데이트를 일괄적으로 처리 => 실제 돔의 리렌더링 연산을 최소화(연쇄적 리플로우, 리페인트 발생 막음)

 

작은 규모의 레이아웃(리플로우)이 여러번 발생하는 것보다

큰 규모의 레이아웃이 한 번 발생하는 것은 성능상의 큰 차이를 나타냄


재조정(Reconciliation)

- 다음 가정을 통해 => 전체 DOM 트리를 탐색하는 시간을(O(n^3))에서 O(n)으로 줄임

1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어냄(엘리먼트가 다르면 비교 안함)

2. key props를 통해 => key가 같은 노드끼리 비교함

 

- 비교 방식

1. 동일한 레벨의 노드들끼리만 비교

 

2. 같은 위치에서 엘리먼트 타입이 다른 경우 => 기존 트리를 제거하고 새롭게 트리를 만든다(내부 엘리먼트, 내부 컴포넌트 모두 제거 후 다시 새로 만듬)

 

3. 같은 위치에서 엘리먼트의 타입이 같은 경우 => class가 변경됐다면 변경된 attributes만 업데이트한다 => 자식 엘리먼트들에 비교알고리즘을 재귀적으로 적용

 

4. 같은 위치에서 '엘리먼트 = 컴포넌트'이고, 타입이 같은경우 => 컴포넌트 인스턴트 자체는 안변함(state 유지) + 라이프사이클 메소드를 이용해 props가 업데이트됨 => render 함수를 호출해서 컴포넌트 이전 엘리먼트 트리와 다음 엘리먼트 트리에 대해 재귀적으로 적용

 

5. 자식 노드에 대한 재귀적 처리 시

 

=> key를 이용해 동일한 key를 갖는 자식들끼리만 비교한다 => 따라서 ItemC 노드만 추가된다. (key가 없으면 두리스트를 모두 순회해야함)

=> 인덱스를 key로 할 경우 => 리스트의 배열이 재정렬되지 않거나 last-child에서만 추가, 변경, 제거가 일어난다면 인덱스를 키로 사용해도됨

=> 하지만 리스트 순서가 바뀌면 key가 전부 바뀌어서 key의 의미가 사라짐

// before
<ul>
  <li>ItemA</li>  //비교대상1
  <li>ItemB</li>  //비교대상2
</ul>

// after
<ul>
  <li>ItemC</li>  //비교대상1 (ItemA -> ItemC 로 변경)
  <li>ItemA</li>  //비교대상2 (ItemB -> ItemA 로 변경)
  <li>ItemB</li>  //비교대상3 (ItemB 추가)
</ul>

 


참고

 

 

[JavaScript] 문서의 로딩되는 시점 이벤트 제어하기 (ft. DOMContentLoaded, load, beforeunload, unload)

문서에 따르면 HTML 문서의 생명주기엔 다음과 같은 3가지 주요 이벤트가 관여한다고 한다. DOMContentLoaded - 브라우저가 HTML을 전부 읽고 DOM 트리를 완성하는 즉시 발생한다. 이미지 파일( )이나 스

mine-it-record.tistory.com

 

모던 JavaScript 튜토리얼

 

ko.javascript.info

 

브라우저의 동작과정부터 React까지

😆 브라우저 동작과정과 React까지 설명입니다.

velog.io

 

CSSOM(CSS Object Model)이란 무엇인가

Javascript의 CSSOM에 대해서 공부하고 정리한 내용입니다.

dkmqflx.github.io

 

 

 

[React] Virtual DOM과 브라우저의 렌더링 과정

리액트의 주요 특징 중 하나는 Virtual DOM을 사용한다는 것입니다.

velog.io

 

 

[React] 가상돔 Virtual DOM이란?

가상 돔(Virtual DOM) 이란? Virtual DOM을 사용하면 실제 DOM에 접근하여 조작하는 대신, 이를 추상화한 자바스크립트 객체를 구성하여 사용 ✅ 실제 DOM의 가벼운 사본 DOM의 상태를 메모리에 저장하고

dev-cini.tistory.com

 

반응형
LIST