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
'JavaScript' 카테고리의 다른 글
브라우저의 Re-Layout, Re-Paint 중 뭐가 더 안좋을까? (0) | 2023.03.09 |
---|---|
자바스크립트의 window 전역객체와 호이스팅은 어떤 관계가 있는가? (0) | 2023.03.09 |
모던자바스크립트 - 6(데이터 타입), 7(연산자), 8(제어문) (0) | 2023.03.07 |
모던 자바스크립트 5 - 표현식과 문 (0) | 2023.02.25 |
모던 자바스크립트 4 - 변수 (0) | 2023.02.20 |